Allow capturing non-public functions as values.
This commit is contained in:
parent
ac4f594063
commit
8216097b2c
@ -58,7 +58,7 @@
|
||||
#define sDEF_LITMAX 500 /* initial size of the literal pool, in "cells" */
|
||||
#define sDEF_AMXSTACK 4096 /* default stack size for AMX files */
|
||||
#define PREPROC_TERM '\x7f'/* termination character for preprocessor expressions (the "DEL" code) */
|
||||
#define sDEF_PREFIX "sourcemod.inc" /* default prefix filename */
|
||||
#define sDEF_PREFIX "xsourcemod.inc" /* default prefix filename */
|
||||
#define sARGS_MAX 32 /* number of arguments a function can have, max */
|
||||
#define sTAGS_MAX 16 /* maximum number of tags on an argument */
|
||||
|
||||
@ -149,6 +149,7 @@ typedef struct s_symbol {
|
||||
int numrefers; /* number of entries in the referrer list */
|
||||
char *documentation; /* optional documentation string */
|
||||
methodmap_t *methodmap; /* if ident == iMETHODMAP */
|
||||
int funcid; /* set for functions during codegen */
|
||||
} symbol;
|
||||
|
||||
/* Possible entries for "ident". These are used in the "symbol", "value"
|
||||
@ -757,6 +758,7 @@ void invoke_setter(struct methodmap_method_s *method, int save);
|
||||
void inc_pri();
|
||||
void dec_pri();
|
||||
void load_hidden_arg();
|
||||
void load_glbfn(symbol *sym);
|
||||
|
||||
/* Code generation functions for arithmetic operators.
|
||||
*
|
||||
@ -981,7 +983,7 @@ typedef struct array_info_s
|
||||
} array_info_t;
|
||||
|
||||
enum FatalError {
|
||||
FIRST_FATAL_ERROR = 182,
|
||||
FIRST_FATAL_ERROR = 183,
|
||||
|
||||
FATAL_ERROR_READ = FIRST_FATAL_ERROR,
|
||||
FATAL_ERROR_WRITE,
|
||||
|
@ -3102,6 +3102,7 @@ symbol *addsym(const char *name,cell addr,int ident,int vclass,int tag,int usage
|
||||
entry.lnumber=fline;
|
||||
entry.numrefers=1;
|
||||
entry.refer=refer;
|
||||
entry.funcid=0;
|
||||
|
||||
/* then insert it in the list */
|
||||
if (vclass==sGLOBAL)
|
||||
|
@ -2403,31 +2403,22 @@ restart:
|
||||
error(76);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int public_index = 0;
|
||||
symbol *target = NULL;
|
||||
for (symbol *iter = glbtab.next; iter; iter = iter->next) {
|
||||
if (iter->ident != iFUNCTN || iter->vclass != sGLOBAL)
|
||||
continue;
|
||||
if (strcmp(iter->name, lval1->sym->name) == 0) {
|
||||
target = iter;
|
||||
break;
|
||||
}
|
||||
if (iter->usage & uPUBLIC)
|
||||
public_index++;
|
||||
}
|
||||
|
||||
if (!target || !(target->usage & uPUBLIC)) {
|
||||
error(76);
|
||||
if (finddepend(sym)) {
|
||||
error(182);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
funcenum_t *fe = funcenum_for_symbol(target);
|
||||
funcenum_t *fe = funcenum_for_symbol(sym);
|
||||
|
||||
// Get address into pri.
|
||||
load_glbfn(sym);
|
||||
|
||||
// New-style "closure".
|
||||
lval1->sym = NULL;
|
||||
lval1->ident = iCONSTEXPR;
|
||||
lval1->constval = (public_index << 1) | 1;
|
||||
lval1->ident = iEXPRESSION;
|
||||
lval1->constval = 0;
|
||||
lval1->tag = fe->tag;
|
||||
target->usage |= uREAD;
|
||||
return FALSE;
|
||||
} /* if */
|
||||
return lvalue;
|
||||
}
|
||||
|
@ -1503,3 +1503,17 @@ void invoke_setter(methodmap_method_t *method, int save)
|
||||
if (sc_status != statSKIP)
|
||||
markusage(method->setter, uREAD);
|
||||
}
|
||||
|
||||
// function value -> pri
|
||||
void load_glbfn(symbol *sym)
|
||||
{
|
||||
assert(sym->ident == iFUNCTN);
|
||||
assert(!(sym->usage & uNATIVE));
|
||||
stgwrite("\tldgfn.pri ");
|
||||
stgwrite(sym->name);
|
||||
stgwrite("\n");
|
||||
code_idx += opcodes(1) + opargs(1);
|
||||
|
||||
if (sc_status != statSKIP)
|
||||
markusage(sym, uREAD);
|
||||
}
|
||||
|
@ -225,6 +225,7 @@ static const char *errmsg[] = {
|
||||
/*179*/ "cannot assign %s[] to %s[], storage classes differ\n",
|
||||
/*180*/ "function return type differs from prototype. expected '%s', but got '%s'\n",
|
||||
/*181*/ "function argument named '%s' differs from prototype\n",
|
||||
/*182*/ "functions that return arrays cannot be used as callbacks\n",
|
||||
#else
|
||||
"\315e\306\227\266k\217:\235\277bu\201fo\220\204\223\012",
|
||||
"\202l\224\250s\205g\346\356e\233\201(\243\315\214\267\202) \253 f\255low ea\305 \042c\353e\042\012",
|
||||
|
@ -37,7 +37,9 @@
|
||||
#include <sclinux.h>
|
||||
#endif
|
||||
#include <am-utility.h>
|
||||
#include <am-string.h>
|
||||
#include <smx/smx-v1.h>
|
||||
#include <smx/smx-v1-opcodes.h>
|
||||
#include <zlib/zlib.h>
|
||||
#include "smx-builder.h"
|
||||
#include "memory-buffer.h"
|
||||
@ -232,6 +234,32 @@ static cell do_dump(Vector<cell> *buffer, char *params, cell opcode)
|
||||
return num * sizeof(cell);
|
||||
}
|
||||
|
||||
static cell do_ldgfen(Vector<cell> *buffer, char *params, cell opcode)
|
||||
{
|
||||
char name[sNAMEMAX+1];
|
||||
|
||||
int i;
|
||||
for (i=0; !isspace(*params); i++,params++) {
|
||||
assert(*params != '\0');
|
||||
assert(i < sNAMEMAX);
|
||||
name[i] = *params;
|
||||
}
|
||||
name[i]='\0';
|
||||
|
||||
symbol *sym = findglb(name, sGLOBAL);
|
||||
assert(sym->ident == iFUNCTN);
|
||||
assert(!(sym->usage & uNATIVE));
|
||||
assert((sym->funcid & 1) == 1);
|
||||
|
||||
if (buffer) {
|
||||
// Note: we emit const.pri for backward compatibility.
|
||||
assert(opcode == sp::OP_UNGEN_LDGFN_PRI);
|
||||
buffer->append(sp::OP_CONST_PRI);
|
||||
buffer->append(sym->funcid);
|
||||
}
|
||||
return opcodes(1) + opargs(1);
|
||||
}
|
||||
|
||||
static cell do_call(Vector<cell> *buffer, char *params, cell opcode)
|
||||
{
|
||||
char name[sNAMEMAX+1];
|
||||
@ -333,6 +361,7 @@ static OPCODEC opcodelist[] = {
|
||||
{112, "dec.pri", sIN_CSEG, parm0 },
|
||||
{115, "dec.s", sIN_CSEG, parm1 },
|
||||
{ 0, "dump", sIN_DSEG, do_dump },
|
||||
{166, "endproc", sIN_CSEG, parm0 },
|
||||
{ 95, "eq", sIN_CSEG, parm0 },
|
||||
{106, "eq.c.alt", sIN_CSEG, parm1 },
|
||||
{105, "eq.c.pri", sIN_CSEG, parm1 },
|
||||
@ -368,6 +397,7 @@ static OPCODEC opcodelist[] = {
|
||||
{128, "jump.pri", sIN_CSEG, parm0 }, /* version 1 */
|
||||
{ 53, "jzer", sIN_CSEG, do_jump },
|
||||
{ 31, "lctrl", sIN_CSEG, parm1 },
|
||||
{167, "ldgfn.pri", sIN_CSEG, do_ldgfen },
|
||||
{ 98, "leq", sIN_CSEG, parm0 },
|
||||
{ 97, "less", sIN_CSEG, parm0 },
|
||||
{ 25, "lidx", sIN_CSEG, parm0 },
|
||||
@ -613,6 +643,18 @@ static int sort_by_addr(const void *a1, const void *a2)
|
||||
return s1->addr - s2->addr;
|
||||
}
|
||||
|
||||
struct function_entry {
|
||||
symbol *sym;
|
||||
AString name;
|
||||
};
|
||||
|
||||
static int sort_functions(const void *a1, const void *a2)
|
||||
{
|
||||
function_entry &f1 = *(function_entry *)a1;
|
||||
function_entry &f2 = *(function_entry *)a2;
|
||||
return strcmp(f1.name.chars(), f2.name.chars());
|
||||
}
|
||||
|
||||
// Helper for parsing a debug string. Debug strings look like this:
|
||||
// L:40 10
|
||||
class DebugString
|
||||
@ -835,6 +877,7 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin)
|
||||
Ref<SmxNameTable> names = new SmxNameTable(".names");
|
||||
|
||||
Vector<symbol *> nativeList;
|
||||
Vector<function_entry> functions;
|
||||
|
||||
// Build the easy symbol tables.
|
||||
for (symbol *sym=glbtab.next; sym; sym=sym->next) {
|
||||
@ -842,10 +885,26 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin)
|
||||
if ((sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr >= 0) {
|
||||
// Natives require special handling, so we save them for later.
|
||||
nativeList.append(sym);
|
||||
} else if ((sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) {
|
||||
sp_file_publics_t &pubfunc = publics->add();
|
||||
pubfunc.address = sym->addr;
|
||||
pubfunc.name = names->add(pool, sym->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((sym->usage & (uPUBLIC|uDEFINE)) == (uPUBLIC|uDEFINE) ||
|
||||
(sym->usage & uREAD))
|
||||
{
|
||||
function_entry entry;
|
||||
entry.sym = sym;
|
||||
if (sym->usage & uPUBLIC) {
|
||||
entry.name = sym->name;
|
||||
} else {
|
||||
// Create a private name.
|
||||
char private_name[sNAMEMAX*3 + 1];
|
||||
snprintf(private_name, sizeof(private_name), ".%d.%s", sym->addr, sym->name);
|
||||
|
||||
entry.name = private_name;
|
||||
}
|
||||
|
||||
functions.append(entry);
|
||||
continue;
|
||||
}
|
||||
} else if (sym->ident==iVARIABLE || sym->ident == iARRAY || sym->ident == iREFARRAY) {
|
||||
if ((sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) {
|
||||
@ -854,7 +913,24 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin)
|
||||
pubvar.name = names->add(pool, sym->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The public list must be sorted.
|
||||
qsort(functions.buffer(), functions.length(), sizeof(function_entry), sort_functions);
|
||||
for (size_t i = 0; i < functions.length(); i++) {
|
||||
function_entry &f = functions[i];
|
||||
symbol *sym = f.sym;
|
||||
|
||||
assert(sym->addr > 0);
|
||||
assert(sym->codeaddr > sym->addr);
|
||||
assert(sym->usage & uDEFINE);
|
||||
|
||||
sp_file_publics_t &pubfunc = publics->add();
|
||||
pubfunc.address = sym->addr;
|
||||
pubfunc.name = names->add(pool, f.name.chars());
|
||||
|
||||
sym->funcid = (uint32_t(i) << 1) | 1;
|
||||
}
|
||||
|
||||
// Shuffle natives to be in address order.
|
||||
qsort(nativeList.buffer(), nativeList.length(), sizeof(symbol *), sort_by_addr);
|
||||
|
@ -129,6 +129,9 @@ class SmxListSection : public SmxSection
|
||||
list_.append(T());
|
||||
return list_.back();
|
||||
}
|
||||
void add(const T &t) {
|
||||
list_.append(t);
|
||||
}
|
||||
bool write(ISmxBuffer *buf) KE_OVERRIDE {
|
||||
return buf->write(list_.buffer(), list_.length() * sizeof(T));
|
||||
}
|
||||
|
16
sourcepawn/compiler/tests/fail-callback-returns-array.sp
Normal file
16
sourcepawn/compiler/tests/fail-callback-returns-array.sp
Normal file
@ -0,0 +1,16 @@
|
||||
String:MyFunction()
|
||||
{
|
||||
new String:egg[10] = "egg";
|
||||
return egg;
|
||||
}
|
||||
|
||||
public crab()
|
||||
{
|
||||
new String:egg[10];
|
||||
egg = MyFunction();
|
||||
}
|
||||
|
||||
public Function:main()
|
||||
{
|
||||
return MyFunction;
|
||||
}
|
@ -0,0 +1 @@
|
||||
(15) : error 182: functions that return arrays cannot be used as callbacks
|
@ -2,4 +2,4 @@
|
||||
(2) : error 141: natives, forwards, and public functions cannot return arrays
|
||||
(3) : error 143: new-style declarations should not have "new"
|
||||
(5) : error 121: cannot specify array dimensions on both type and name
|
||||
(11) : error 025: function heading differs from prototype
|
||||
(11) : error 180: function return type differs from prototype. expected 'void', but got 'int'
|
||||
|
@ -235,6 +235,7 @@ namespace sp {
|
||||
_(STRADJUST_PRI, "stradjust.pri") \
|
||||
_(UNGEN_STKADJUST,"stackadjust") \
|
||||
_(ENDPROC, "endproc") \
|
||||
_(UNGEN_LDGFN_PRI,"ldgfn.pri") \
|
||||
_(FABS, "fabs") \
|
||||
_(FLOAT, "float") \
|
||||
_(FLOATADD, "float.add") \
|
||||
|
Loading…
Reference in New Issue
Block a user