Allow capturing non-public functions as values.

This commit is contained in:
David Anderson 2015-01-04 17:13:58 -08:00
parent ac4f594063
commit 8216097b2c
11 changed files with 134 additions and 28 deletions

View File

@ -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,

View File

@ -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)

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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",

View File

@ -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) {
@ -856,6 +915,23 @@ static void assemble_to_buffer(MemoryBuffer *buffer, void *fin)
}
}
// 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);
for (size_t i = 0; i < nativeList.length(); i++) {

View File

@ -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));
}

View 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;
}

View File

@ -0,0 +1 @@
(15) : error 182: functions that return arrays cannot be used as callbacks

View File

@ -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'

View File

@ -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") \