diff --git a/sourcepawn/compiler/sc.h b/sourcepawn/compiler/sc.h index ae1b06e3..2d40d61d 100644 --- a/sourcepawn/compiler/sc.h +++ b/sourcepawn/compiler/sc.h @@ -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, diff --git a/sourcepawn/compiler/sc2.cpp b/sourcepawn/compiler/sc2.cpp index f2523586..4fccaeef 100644 --- a/sourcepawn/compiler/sc2.cpp +++ b/sourcepawn/compiler/sc2.cpp @@ -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) diff --git a/sourcepawn/compiler/sc3.cpp b/sourcepawn/compiler/sc3.cpp index 36f6c7a0..857ccb78 100644 --- a/sourcepawn/compiler/sc3.cpp +++ b/sourcepawn/compiler/sc3.cpp @@ -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; } diff --git a/sourcepawn/compiler/sc4.cpp b/sourcepawn/compiler/sc4.cpp index a4154ee2..3cee7511 100644 --- a/sourcepawn/compiler/sc4.cpp +++ b/sourcepawn/compiler/sc4.cpp @@ -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); +} diff --git a/sourcepawn/compiler/sc5-in.scp b/sourcepawn/compiler/sc5-in.scp index c4ab21d1..27739fa7 100644 --- a/sourcepawn/compiler/sc5-in.scp +++ b/sourcepawn/compiler/sc5-in.scp @@ -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", diff --git a/sourcepawn/compiler/sc6.cpp b/sourcepawn/compiler/sc6.cpp index a87106fc..67ed56ce 100644 --- a/sourcepawn/compiler/sc6.cpp +++ b/sourcepawn/compiler/sc6.cpp @@ -37,7 +37,9 @@ #include #endif #include +#include #include +#include #include #include "smx-builder.h" #include "memory-buffer.h" @@ -232,6 +234,32 @@ static cell do_dump(Vector *buffer, char *params, cell opcode) return num * sizeof(cell); } +static cell do_ldgfen(Vector *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 *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 names = new SmxNameTable(".names"); Vector nativeList; + Vector 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); diff --git a/sourcepawn/compiler/smx-builder.h b/sourcepawn/compiler/smx-builder.h index 57b1eb6e..22295bab 100644 --- a/sourcepawn/compiler/smx-builder.h +++ b/sourcepawn/compiler/smx-builder.h @@ -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)); } diff --git a/sourcepawn/compiler/tests/fail-callback-returns-array.sp b/sourcepawn/compiler/tests/fail-callback-returns-array.sp new file mode 100644 index 00000000..fee720f5 --- /dev/null +++ b/sourcepawn/compiler/tests/fail-callback-returns-array.sp @@ -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; +} diff --git a/sourcepawn/compiler/tests/fail-callback-returns-array.txt b/sourcepawn/compiler/tests/fail-callback-returns-array.txt new file mode 100644 index 00000000..da6bec96 --- /dev/null +++ b/sourcepawn/compiler/tests/fail-callback-returns-array.txt @@ -0,0 +1 @@ +(15) : error 182: functions that return arrays cannot be used as callbacks diff --git a/sourcepawn/compiler/tests/fail-newdecls.txt b/sourcepawn/compiler/tests/fail-newdecls.txt index dcf90e6a..1eb77d2a 100644 --- a/sourcepawn/compiler/tests/fail-newdecls.txt +++ b/sourcepawn/compiler/tests/fail-newdecls.txt @@ -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' diff --git a/sourcepawn/include/smx/smx-v1-opcodes.h b/sourcepawn/include/smx/smx-v1-opcodes.h index c50a7124..51d42329 100644 --- a/sourcepawn/include/smx/smx-v1-opcodes.h +++ b/sourcepawn/include/smx/smx-v1-opcodes.h @@ -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") \