Skip to content

Commit

Permalink
Fix nativeidxof and return tags.
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Cole committed Nov 5, 2023
1 parent 1b82137 commit 950e088
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 57 deletions.
21 changes: 11 additions & 10 deletions YSI_Server/y_decorator/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ First we define our `@example` decorator as an alias for `@decorator__(DECORATOR
`DECORATOR_EXAMPLE` is where we will actually do our code generation once the code analysis completes. The parameters given to `DECORATOR_EXAMPLE` are:

* `%0` - Anything given to `@example(params)` directly (i.e. the decorator's parameters).
* `%1` - The function name.
* `%2` - The function's parameters.
* `%3` - The specifier, i.e. the `"sif"`-like string you'd need to call this function with `CallRemoteFunction`.
* `%4` - The function parameters again, but stripped of syntax; for calling the function.
* `%1` - The function's return tag (including `:`).
* `%2` - The function name.
* `%3` - The function's parameters.
* `%4` - The specifier, i.e. the `"sif"`-like string you'd need to call this function with `CallRemoteFunction`.
* `%5` - The function parameters again, but stripped of syntax; for calling the function.
* `%8` - Internal details. Make sure you keep this!

A call of:
Expand All @@ -32,15 +33,15 @@ DECORATOR_EXAMPLE:%8$(4, 6)(MyFunc)(Float:f, const string:s[], d)(fsi)(f, s, d)
These are the most common data needed for writing custom decorators. If you need to do some more custom tag-based processing of things you can do it safely before `%8$`:

```pawn
#define DECORATOR_EXAMPLE:%8$(%0)(%1)(%2)(%3)(%4) EXAMPLE_NO_PARAMS:EXAMPLE_W_PARAMS:%8$(%0)(%1)(%2)(%3)(%4)
#define EXAMPLE_NO_PARAMS:%8$()(%1)(%2)(%3)(%4) %8$ /* whatever - don't forget `%8$`! */
#define EXAMPLE_W_PARAMS:%8$(%0)(%1)(%2)(%3)(%4) %8$ /* whatever - don't forget `%8$`! */
#define DECORATOR_EXAMPLE:%8$(%0)(%1)(%2)(%3)(%4)(%5) EXAMPLE_NO_PARAMS:EXAMPLE_W_PARAMS:%8$(%0)(%1)(%2)(%3)(%4)(%5)
#define EXAMPLE_NO_PARAMS:%8$()(%1)(%2)(%3)(%4)(%5) %8$ /* whatever - don't forget `%8$`! */
#define EXAMPLE_W_PARAMS:%8$(%0)(%1)(%2)(%3)(%4)(%5) %8$ /* whatever - don't forget `%8$`! */
```

Now we need to actually do something with all this data. To run some code at server start we invoke another decorator (`@init`), which is perfectly safe. Remember to always start the output with `%8$`, just for implementation reasons (see the code-parse.inc documentation for reasons why):

```pawn
#define DECORATOR_EXAMPLE:%8$(%0)(%1)(%2)(%3)(%4) %8$@init %1() printf("Function %s, with parameters %s, decorated", __nameof(%1), #%3); %1(%2)
#define DECORATOR_EXAMPLE:%8$(%0)(%1)(%2)(%3)(%4)(%5) %8$@init %2() printf("Function %s, with parameters %s, decorated", __nameof(%2), #%4); %1%2(%3)
```

## Example 2
Expand All @@ -50,7 +51,7 @@ We can make a function called remotely with:
```pawn
#define @remote @decorator__(DECORATOR_REMOTE)
#define DECORATOR_REMOTE:%8$(%0)(%1)(%2)(%3)(%4) %8$forward @@%1(%2); %1(%2) CallRemoteFunction(__nameof(@@%1), _:#%3, %4); public @@%1(%2)
#define DECORATOR_REMOTE:%8$(%0)(%1)(%2)(%3)(%4)(%5) %8$forward @@%2(%3); %1%2(%3) CallRemoteFunction(__nameof(@@%2), _:#%4, %5); public @@%2(%3)
```

Used as:
Expand All @@ -66,5 +67,5 @@ main()
}
```

`@@` can be used as a prefix for public functions that will consume excess space to make valid symbols. `_:#%3,` is a trick to remove the trailing comma in the case of no parameters. `__nameof` is not really needed here since we are generating the code automatically, but unlike `#@@%1` to convert the function name in to a string, `__nameof` will give an error if the function being stringised doesn't exist.
`@@` can be used as a prefix for public functions that will consume excess space to make valid symbols. `_:#%4,` is a trick to remove the trailing comma in the case of no parameters. `__nameof` is not really needed here since we are generating the code automatically, but unlike `#@@%1` to convert the function name in to a string, `__nameof` will give an error if the function being stringised doesn't exist.

50 changes: 10 additions & 40 deletions YSI_Server/y_decorator/y_decorator_entry.inc
Original file line number Diff line number Diff line change
Expand Up @@ -79,48 +79,18 @@ Optional plugins:
#include "..\..\YSI_Core\y_utils"
#include "..\y_thirdpartyinclude\y_codeparse"

#define @decorator__(%2)(%3)%0(%1) FUNC_PARSER(DECORATOR__,ARR_CST:STR_CST:NUM_CST:)(%0(%1))(%0)(%1)()()(%2)(%3)
#define @decorator__(%2)(%3)%0(%1) FUNC_PARSER(DECORATOR__,ARR_CST:STR_CST:NUM_CST:RET_TAG:)(%0(%1))(%1)()()(%2)(%3)

#define DECORATOR___ARR(%9,%9,%5,%9)%8$(%0)(%1)(%6)(%7) %8$(%0)(%1)(%6a)(%7,%5)
#define DECORATOR___STR(%9,%9,%5,%9)%8$(%0)(%1)(%6)(%7) %8$(%0)(%1)(%6s)(%7,%5)
#define DECORATOR___NUM(%9,%9,%5)%8$(%0)(%1)(%6)(%7) %8$(%0)(%1)(%6i)(%7,%5)
#define DECORATOR___ARR(%9,%9,%5,%9)%8$(%1)(%6)(%7) %8$(%1)(%6a)(%7,%5)
#define DECORATOR___STR(%9,%9,%5,%9)%8$(%1)(%6)(%7) %8$(%1)(%6s)(%7,%5)
#define DECORATOR___NUM(%9,%9,%5)%8$(%1)(%6)(%7) %8$(%1)(%6i)(%7,%5)

#define DECORATOR___END(%9)%8$(%0)(%1)(%6)(,%7)(%2)(%3) %2:%8$(%3)(%0)(%1)(%6)(%7)
#define DECORATOR___NUL(%9)%8$(%0)()()()(%2)(%3) %2:%8$(%3)(%0)(%1)()()
#define DECORATOR___END(%0)%8$(%1)(%6)(,%7)(%2)(%3) %2:%8$(%3)(_:)(%0)(%1)(%6)(%7)
#define DECORATOR___NUL(%0)%8$()()()(%2)(%3) %2:%8$(%3)(_:)(%0)(%1)()()

#endinput
#define DECORATOR___END_TAG(%5,%0)%8$(%1)(%6)(,%7)(%2)(%3) %2:%8$(%3)(%5)(%0)(%1)(%6)(%7)
#define DECORATOR___NUL_TAG(%5,%0)%8$()()()(%2)(%3) %2:%8$(%3)(%5)(%0)(%1)()()

// Usage:
#define @example @decorator__(DECORATOR_EXAMPLE)
// The parameters given to `DECORATOR_EXAMPLE` are:
//
// %0 - Anything given to `@example(params)` directly.
// %1 - The function name.
// %2 - The function parameters.
// %3 - The specifier, i.e. the `"sif"`-like string you'd need to call this
// function with `CallRemoteFunction`.
// %4 - The function parameters again, but stripped of syntax; for calling
// the function.
// %8 - Internal details. Make sure you keep this!
//
// Example:
//
// @example(4, 6) MyFunc(Float:f, const string:s[], d)
//
// Becomes:
//
// DECORATOR_EXAMPLE:%8$(4, 6)(MyFunc)(Float:f, const string:s[], d)(fsi)(f, s, d)
//
// These are the most common data needed for writing custom decorators. If you
// need to do some more custom tag-based processing of things you can do it
// safely before `%8$`:
//
// #define DECORATOR_EXAMPLE:%8$(%0)(%1)(%2)(%3)(%4) EXAMPLE_NO_PARAMS:EXAMPLE_W_PARAMS:%8$(%0)(%1)(%2)(%3)(%4)
// #define EXAMPLE_NO_PARAMS:%8$()(%1)(%2)(%3)(%4) %8$ /* whatever - don't forget `%8$`! */
// #define EXAMPLE_W_PARAMS:%8$(%0)(%1)(%2)(%3)(%4) %8$ /* whatever - don't forget `%8$`! */
//

#define DECORATOR_EXAMPLE:%8$(%0)(%1)(%2)(%3)(%4)

#define @decorator__(%0)(%1)%2(%3)
#define DECORATOR___END_VOD(%0)%8$(%1)(%6)(,%7)(%2)(%3) %2:%8$(%3)(void:)(%0)(%1)(%6)(%7)
#define DECORATOR___NUL_VOD(%0)%8$()()()(%2)(%3) %2:%8$(%3)(void:)(%0)(%1)()()

30 changes: 24 additions & 6 deletions YSI_Server/y_natives/y_natives_entry.inc
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Optional plugins:
#include "..\y_decorator"
#include "..\y_serverdata"
#include "..\y_thirdpartyinclude\y_codeparse"
#include "..\y_thirdpartyinclude\y_amx-assembly"

/*
@native("CreateDynamicCircle") STREAMER_TAG_AREA:MAYBE_CreateDynamicCircle(Float:x, Float:y, Float:size, worldid = -1, interiorid = -1, playerid = -1, priority = 0);
Expand All @@ -95,7 +96,9 @@ if (defined("MAYBE_CreateDynamicCircle"))
#define @native(%3)%0(%1); @decorator__(NATIVE_DECORATOR)(%3)%0(%1)

// TODO: `void:` and `string:` return support.
#define NATIVE_DECORATOR:%8$("%0")(%1)(%2)(%3)(%4) %8$native _yN@%1(%2)=heapspace;stock %1(%2){static s=1;if(s)s=yN@_(#%0,nativeidxof(_yN@%1<%3>));return _yN@%1(%4);}
#define NATIVE_IDXOF(%0(%1)) (_:((O@B_())?(((%0(%1)),O@V_)?1:2):(O@V_)))

#define NATIVE_DECORATOR:%8$("%0")(%1)(%2)(%3)(%4)(%5) %8$native %1_yN@%2(%3)=heapspace;stock %1%2(%3){static s=1;if(s)s=yN@_(#%0,NATIVE_IDXOF(_yN@%2(%5)));return _yN@%2(%5);}

stock yN@_(const string:name[], idx)
{
Expand All @@ -105,22 +108,37 @@ stock yN@_(const string:name[], idx)
addrHigh;
if (Server_GetNative(name, addrLow, addrHigh))
{
//printf("Found native %d (%d) with address %d, %d", idx, AMX_GetEntryFromNativeIndex(idx), addrLow, addrHigh);
// The native exists. Write both components of the address pointer,
// despite the fact that we may only need one depending on what server
// bit-width we're running on (not worth working it out).
idx = AMX_GetEntryFromNativeIndex(idx),
AMX_Write(idx, addrLow);
#if cellbits == 32
// If we are running on a 32-bit machine this code does nothing. If
// we are running on a 64-bit machine this will write the second
// cell of the stored pointer. Pointer entires are `cellbits + 32`
// long, not a constant two cells. If this were 64-bit we'd need to
// do awkward masking to write the upper bytes. However, if this
// were a 64-bit script we wouldn't need to write the second address
// at all so there's no danger of clobbering data in either case.
AMX_Write(idx + 4, addrHigh);
#else
#pragma unused addrHigh
#endif
}
else
{
// Don't replace anything. The server will fall back to calling
// `heapspace`, which is essentially a no-op.
P:F("Attempting to call non-existant native `%s`", name);
Debug_PrintF("Attempting to call non-existant native `%s`", name);
}

// If there is one, insert it as the pointer for native `idx`.

// Otherwise show an error.

// Never repeat this code.
return 0;
}

#if defined YSI_TESTS
#include "y_natives_tests"
#endif

80 changes: 80 additions & 0 deletions YSI_Server/y_natives/y_natives_tests.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
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 YSI framework.
The Initial Developer of the Original Code is Alex "Y_Less" Cole.
Portions created by the Initial Developer are Copyright (c) 2022
the Initial Developer. All Rights Reserved.
Contributors:
Y_Less
koolk
JoeBullet/Google63
g_aSlice/Slice
Misiur
samphunter
tianmeta
maddinat0r
spacemud
Crayder
Dayvison
Ahmad45123
Zeex
irinel1996
Yiin-
Chaprnks
Konstantinos
Masterchen09
Southclaws
PatchwerkQWER
m0k1
paulommu
udan111
Cheaterman
Thanks:
JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
ZeeX - Very productive conversations.
koolk - IsPlayerinAreaEx code.
TheAlpha - Danish translation.
breadfish - German translation.
Fireburn - Dutch translation.
yom - French translation.
50p - Polish translation.
Zamaroht - Spanish translation.
Los - Portuguese 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.
Optional plugins:
Gamer_Z - GPS.
Incognito - Streamer.
Me - sscanf2, fixes2, Whirlpool.
*/

@native("floatmul") Float:NATIVE_floatmul(Float:a, Float:b);

@test(.group = "y_natives") NATIVE_floatmul()
{
ASSERT_EQ(NATIVE_floatmul(5.0, 6.0), 30.0);
}

2 changes: 1 addition & 1 deletion amx
Submodule amx updated from 1a4eee to 28bacb

0 comments on commit 950e088

Please sign in to comment.