diff --git a/extensions/topmenus/smn_topmenus.cpp b/extensions/topmenus/smn_topmenus.cpp index 247c0db9..3dc0e380 100644 --- a/extensions/topmenus/smn_topmenus.cpp +++ b/extensions/topmenus/smn_topmenus.cpp @@ -449,6 +449,21 @@ static cell_t TopMenu_AddCategory(IPluginContext *pContext, const cell_t *params return AddToTopMenu(pContext, new_params); } +static cell_t TopMenu_FromHandle(IPluginContext *pContext, const cell_t *params) +{ + HandleError err; + TopMenu *pMenu; + HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity()); + + if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err); + } + + return params[1]; +} + sp_nativeinfo_t g_TopMenuNatives[] = { {"AddToTopMenu", AddToTopMenu}, @@ -474,6 +489,7 @@ sp_nativeinfo_t g_TopMenuNatives[] = {"TopMenu.GetInfoString", GetTopMenuInfoString}, {"TopMenu.GetObjName", GetTopMenuName}, {"TopMenu.CacheTitles.set", SetTopMenuTitleCaching}, + {"TopMenu.FromHandle", TopMenu_FromHandle}, {NULL, NULL}, }; diff --git a/plugins/basebans.sp b/plugins/basebans.sp index 0ecb2bd0..99498c0d 100644 --- a/plugins/basebans.sp +++ b/plugins/basebans.sp @@ -124,8 +124,10 @@ LoadBanReasons() } } -public OnAdminMenuReady(TopMenu topmenu) +public OnAdminMenuReady(Handle aTopMenu) { + TopMenu topmenu = TopMenu.FromHandle(aTopMenu); + /* Block us from being called twice */ if (topmenu == hTopMenu) { diff --git a/plugins/basecomm.sp b/plugins/basecomm.sp index 6c2993e7..0b176198 100644 --- a/plugins/basecomm.sp +++ b/plugins/basecomm.sp @@ -100,8 +100,10 @@ public OnPluginStart() } } -public OnAdminMenuReady(TopMenu topmenu) +public OnAdminMenuReady(Handle aTopMenu) { + TopMenu topmenu = TopMenu.FromHandle(aTopMenu); + /* Block us from being called twice */ if (topmenu == hTopMenu) { diff --git a/plugins/basecommands.sp b/plugins/basecommands.sp index 5bc80f6d..24593358 100644 --- a/plugins/basecommands.sp +++ b/plugins/basecommands.sp @@ -146,8 +146,10 @@ bool:IsClientAllowedToChangeCvar(client, const String:cvarname[]) return allowed; } -public OnAdminMenuReady(TopMenu topmenu) +public OnAdminMenuReady(Handle aTopMenu) { + TopMenu topmenu = TopMenu.FromHandle(aTopMenu); + /* Block us from being called twice */ if (topmenu == hTopMenu) { diff --git a/plugins/basevotes.sp b/plugins/basevotes.sp index 943a5e5d..5aea62a8 100644 --- a/plugins/basevotes.sp +++ b/plugins/basevotes.sp @@ -132,8 +132,10 @@ public OnConfigsExecuted() g_mapCount = LoadMapList(g_MapList); } -public OnAdminMenuReady(TopMenu topmenu) +public OnAdminMenuReady(Handle aTopMenu) { + TopMenu topmenu = TopMenu.FromHandle(aTopMenu); + /* Block us from being called twice */ if (topmenu == hTopMenu) { diff --git a/plugins/funcommands.sp b/plugins/funcommands.sp index 78dcb295..8b92efbe 100644 --- a/plugins/funcommands.sp +++ b/plugins/funcommands.sp @@ -258,8 +258,10 @@ public Action:Event_RoundEnd(Handle:event,const String:name[],bool:dontBroadcast KillAllDrugs(); } -public OnAdminMenuReady(TopMenu topmenu) +public OnAdminMenuReady(Handle aTopMenu) { + TopMenu topmenu = TopMenu.FromHandle(aTopMenu); + /* Block us from being called twice */ if (topmenu == hTopMenu) { diff --git a/plugins/funvotes.sp b/plugins/funvotes.sp index 2d2c6e2e..d975ab2c 100644 --- a/plugins/funvotes.sp +++ b/plugins/funvotes.sp @@ -135,8 +135,10 @@ public OnPluginStart() } } -public OnAdminMenuReady(TopMenu topmenu) +public OnAdminMenuReady(Handle aTopMenu) { + TopMenu topmenu = TopMenu.FromHandle(aTopMenu); + /* Block us from being called twice */ if (topmenu == hTopMenu) { diff --git a/plugins/include/adminmenu.inc b/plugins/include/adminmenu.inc index db5cacdb..b3662c04 100644 --- a/plugins/include/adminmenu.inc +++ b/plugins/include/adminmenu.inc @@ -65,7 +65,7 @@ * @param topmenu Handle to the admin menu's TopMenu. * @noreturn */ -forward OnAdminMenuCreated(TopMenu topmenu); +forward OnAdminMenuCreated(Handle topmenu); /** * Called when the admin menu is ready to have items added. @@ -73,7 +73,7 @@ forward OnAdminMenuCreated(TopMenu topmenu); * @param topmenu Handle to the admin menu's TopMenu. * @noreturn */ -forward OnAdminMenuReady(TopMenu topmenu); +forward OnAdminMenuReady(Handle topmenu); /** * Retrieves the Handle to the admin top menu. diff --git a/plugins/include/topmenus.inc b/plugins/include/topmenus.inc index 83f99ac1..817dec51 100644 --- a/plugins/include/topmenus.inc +++ b/plugins/include/topmenus.inc @@ -146,6 +146,11 @@ methodmap TopMenu < Handle // @return A new TopMenu. public native TopMenu(TopMenuHandler handler); + // Returns a TopMenu handle from a generic handle. If the given handle is + // a TopMenu, the handle is simply casted back. Otherwise, an error is + // raised. + public static native TopMenu FromHandle(Handle handle); + // Re-sorts the items in a TopMenu via a configuration file. // // The format of the configuration file should be a Valve Key-Values diff --git a/plugins/playercommands.sp b/plugins/playercommands.sp index f0ab772e..7ddcee54 100644 --- a/plugins/playercommands.sp +++ b/plugins/playercommands.sp @@ -76,8 +76,10 @@ public OnPluginStart() } } -public OnAdminMenuReady(TopMenu topmenu) +public OnAdminMenuReady(Handle aTopMenu) { + TopMenu topmenu = TopMenu.FromHandle(aTopMenu); + /* Block us from being called twice */ if (topmenu == hTopMenu) { diff --git a/sourcepawn/compiler/sc.h b/sourcepawn/compiler/sc.h index d22c1b54..164f64bd 100644 --- a/sourcepawn/compiler/sc.h +++ b/sourcepawn/compiler/sc.h @@ -103,6 +103,8 @@ typedef struct s_constvalue { * tag for enumeration lists */ } constvalue; +struct methodmap_t; + /* Symbol table format * * The symbol name read from the input file is stored in "name", the @@ -116,7 +118,6 @@ typedef struct s_constvalue { typedef struct s_symbol { struct s_symbol *next; struct s_symbol *parent; /* hierarchical types */ - struct s_symbol *target; /* proxy target */ char name[sNAMEMAX+1]; uint32_t hash; /* value derived from name, for quicker searching */ cell addr; /* address or offset (or value for constant, index for native function) */ @@ -151,6 +152,7 @@ typedef struct s_symbol { struct s_symbol **refer; /* referrer list, functions that "use" this symbol */ int numrefers; /* number of entries in the referrer list */ char *documentation; /* optional documentation string */ + methodmap_t *methodmap; /* if ident == iMETHODMAP */ } symbol; /* Possible entries for "ident". These are used in the "symbol", "value" @@ -170,8 +172,8 @@ typedef struct s_symbol { #define iFUNCTN 9 #define iREFFUNC 10 #define iVARARGS 11 /* function specified ... as argument(s) */ -#define iPROXY 12 /* proxies to another symbol. */ #define iACCESSOR 13 /* property accessor via a methodmap_method_t */ +#define iMETHODMAP 14 /* symbol defining a methodmap */ /* Possible entries for "usage" * @@ -227,7 +229,6 @@ typedef struct s_symbol { #define uRETNONE 0x10 #define flgDEPRECATED 0x01 /* symbol is deprecated (avoid use) */ -#define flgPROXIED 0x02 /* symbol has incoming proxy */ #define uCOUNTOF 0x20 /* set in the "hasdefault" field of the arginfo struct */ #define uTAGOF 0x40 /* set in the "hasdefault" field of the arginfo struct */ @@ -246,7 +247,6 @@ struct methodmap_method_s; typedef struct value_s { symbol *sym; /* symbol in symbol table, NULL for (constant) expression */ - symbol *proxy; /* original symbol if resolved via a proxy */ cell constval; /* value of the constant expression (if ident==iCONSTEXPR) * also used for the size of a literal array */ int tag; /* tag (of the expression) */ @@ -678,7 +678,7 @@ void delete_symbol(symbol *root,symbol *sym); void delete_symbols(symbol *root,int level,int del_labels,int delete_functions); int refer_symbol(symbol *entry,symbol *bywhom); void markusage(symbol *sym,int usage); -symbol *findglb(const char *name,int filter,symbol **alias = NULL); +symbol *findglb(const char *name,int filter); symbol *findloc(const char *name); symbol *findconst(const char *name,int *matchtag); symbol *finddepend(const symbol *parent); diff --git a/sourcepawn/compiler/sc1.cpp b/sourcepawn/compiler/sc1.cpp index 65875021..892cd32b 100644 --- a/sourcepawn/compiler/sc1.cpp +++ b/sourcepawn/compiler/sc1.cpp @@ -3570,32 +3570,6 @@ static void check_void_decl(const declinfo_t *decl, int variable) } } -static void define_constructor(methodmap_t *map, methodmap_method_t *method) -{ - symbol *sym = findglb(map->name, sGLOBAL); - - if (sym) { - const char *type = "__unknown__"; - switch (sym->ident) { - case iVARIABLE: - case iARRAY: - case iARRAYCELL: - case iARRAYCHAR: - type = "variable"; - break; - case iFUNCTN: - type = "function"; - break; - } - error(113, map->name, type); - return; - } - - sym = addsym(map->name, 0, iPROXY, sGLOBAL, 0, 0); - sym->target = method->target; - method->target->flags |= flgPROXIED; -} - // Current lexer position is, we've parsed "public", an optional "native", and // a type expression. // @@ -3651,7 +3625,13 @@ static void make_primitive(typeinfo_t *type, int tag) type->ident = iVARIABLE; } -symbol *parse_inline_function(methodmap_t *map, const typeinfo_t *type, const char *name, int is_native, int is_ctor, int is_dtor) +symbol *parse_inline_function(methodmap_t *map, + const typeinfo_t *type, + const char *name, + int is_native, + int is_ctor, + int is_dtor, + bool is_static) { declinfo_t decl; memset(&decl, 0, sizeof(decl)); @@ -3666,7 +3646,7 @@ symbol *parse_inline_function(methodmap_t *map, const typeinfo_t *type, const ch decl.type.is_new = TRUE; const int *thistag = NULL; - if (!is_ctor) + if (!is_ctor && !is_static) thistag = &map->tag; // Build a new symbol. Construct a temporary name including the class. @@ -3778,7 +3758,7 @@ int parse_property_accessor(const typeinfo_t *type, methodmap_t *map, methodmap_ ret_type = &voidtype; } - target = parse_inline_function(map, ret_type, tmpname, is_native, FALSE, FALSE); + target = parse_inline_function(map, ret_type, tmpname, is_native, FALSE, FALSE, false); } if (!target) @@ -3877,8 +3857,12 @@ methodmap_method_t *parse_method(methodmap_t *map) int is_dtor = 0; int is_bind = 0; int is_native = 0; + bool is_static = false; const char *spectype = layout_spec_name(map->spec); + if (matchtoken(tSTATIC)) + is_static = true; + // This stores the name of the method (for destructors, we add a ~). token_ident_t ident; strcpy(ident.name, "__unknown__"); @@ -3890,7 +3874,8 @@ methodmap_method_t *parse_method(methodmap_t *map) typeinfo_t type; memset(&type, 0, sizeof(type)); - if (matchtoken('~')) { + // Destructors cannot be static. + if (!is_static && matchtoken('~')) { // We got something like "public ~Blah = X" is_bind = TRUE; is_dtor = TRUE; @@ -3977,6 +3962,11 @@ methodmap_method_t *parse_method(methodmap_t *map) error(114, "constructor", spectype, map->name); } + if (is_ctor && is_static) { + // Constructors may not be static. + error(175); + } + symbol *target = NULL; if (is_bind) { // Find an existing symbol. @@ -3986,7 +3976,7 @@ methodmap_method_t *parse_method(methodmap_t *map) else if (target->ident != iFUNCTN) error(10); } else { - target = parse_inline_function(map, &type, ident.name, is_native, is_ctor, is_dtor); + target = parse_inline_function(map, &type, ident.name, is_native, is_ctor, is_dtor, is_static); } if (!target) @@ -4024,12 +4014,15 @@ methodmap_method_t *parse_method(methodmap_t *map) method->target = target; method->getter = NULL; method->setter = NULL; + method->is_static = is_static; // If the symbol is a constructor, we bypass the initial argument checks. if (is_ctor) { - define_constructor(map, method); - } else if (!check_this_tag(map, target)) { - error(108, spectype, map->name); + if (map->ctor) + error(113, map->name); + } else if (!is_static) { + if (!check_this_tag(map, target)) + error(108, spectype, map->name); } if (is_dtor) @@ -4049,7 +4042,6 @@ static void domethodmap(LayoutSpec spec) { int val; char *str; - LayoutSpec old_spec; methodmap_t *parent = NULL; const char *spectype = layout_spec_name(spec); @@ -4062,8 +4054,9 @@ static void domethodmap(LayoutSpec spec) if (!isupper(*mapname)) error(109, spectype); - old_spec = deduce_layout_spec_by_name(mapname); - if (!can_redef_layout_spec(spec, old_spec)) + LayoutSpec old_spec = deduce_layout_spec_by_name(mapname); + int can_redef = can_redef_layout_spec(spec, old_spec); + if (!can_redef) error(110, mapname, layout_spec_name(old_spec)); if (matchtoken('<')) { @@ -4093,6 +4086,46 @@ static void domethodmap(LayoutSpec spec) } methodmap_add(map); + if (can_redef) { + symbol *sym = findglb(mapname, sGLOBAL); + if (sym && sym->ident != iMETHODMAP) { + // We should only hit this on the first pass. Assert really hard that + // we're about to kill an enum definition and not something random. + assert(sc_status == statFIRST); + assert(sym->ident == iCONSTEXPR); + assert(TAGID(map->tag) == TAGID(sym->tag)); + + sym->ident = iMETHODMAP; + + // Kill previous enumstruct properties, if any. + if (sym->usage & uENUMROOT) { + for (constvalue *cv = sym->dim.enumlist; cv; cv = cv->next) { + symbol *csym = findglb(cv->name, sGLOBAL); + if (csym && + csym->ident == iCONSTEXPR && + csym->parent == sym && + (csym->usage & uENUMFIELD)) + { + csym->usage &= ~uENUMFIELD; + csym->parent = NULL; + } + } + delete_consttable(sym->dim.enumlist); + free(sym->dim.enumlist); + sym->dim.enumlist = NULL; + } + } else if (!sym) { + sym = addsym( + mapname, // name + 0, // addr + iMETHODMAP, // ident + sGLOBAL, // vclass + map->tag, // tag + uDEFINE); // usage + } + sym->methodmap = map; + } + needtoken('{'); while (!matchtoken('}')) { token_t tok; @@ -4587,8 +4620,8 @@ static void decl_enum(int vclass) int tag,explicittag; cell increment,multiplier; constvalue *enumroot; - symbol *enumsym; LayoutSpec spec; + symbol *enumsym = nullptr; /* get an explicit tag, if any (we need to remember whether an explicit * tag was passed, even if that explicit tag was "_:", so we cannot call @@ -4655,19 +4688,35 @@ static void decl_enum(int vclass) } /* if */ if (strlen(enumname)>0) { - /* already create the root symbol, so the fields can have it as their "parent" */ - enumsym=add_constant(enumname,0,vclass,tag); - if (enumsym!=NULL) - enumsym->usage |= uENUMROOT; - /* start a new list for the element names */ - if ((enumroot=(constvalue*)malloc(sizeof(constvalue)))==NULL) - error(FATAL_ERROR_OOM); /* insufficient memory (fatal error) */ - memset(enumroot,0,sizeof(constvalue)); + if (vclass == sGLOBAL) { + if ((enumsym = findglb(enumname, vclass)) != NULL) { + // If we were previously defined as a methodmap, don't overwrite the + // symbol. Otherwise, flow into add_constant where we will error. + if (enumsym->ident != iMETHODMAP) + enumsym = nullptr; + } + } + + if (!enumsym) { + /* create the root symbol, so the fields can have it as their "parent" */ + enumsym=add_constant(enumname,0,vclass,tag); + if (enumsym!=NULL) + enumsym->usage |= uENUMROOT; + /* start a new list for the element names */ + if ((enumroot=(constvalue*)malloc(sizeof(constvalue)))==NULL) + error(FATAL_ERROR_OOM); /* insufficient memory (fatal error) */ + memset(enumroot,0,sizeof(constvalue)); + } } else { enumsym=NULL; enumroot=NULL; } /* if */ + // If this enum is for a methodmap, forget the symbol so code below doesn't + // build an enum struct. + if (enumsym && enumsym->ident == iMETHODMAP) + enumsym = NULL; + needtoken('{'); /* go through all constants */ value=0; /* default starting value */ @@ -4721,7 +4770,7 @@ static void decl_enum(int vclass) matchtoken(';'); /* eat an optional ; */ /* set the enum name to the "next" value (typically the last value plus one) */ - if (enumsym!=NULL) { + if (enumsym) { assert((enumsym->usage & uENUMROOT)!=0); enumsym->addr=value; /* assign the constant list */ @@ -6510,8 +6559,8 @@ static int testsymbols(symbol *root,int level,int testlabs,int testconst) error(203,sym->name); /* symbol isn't used: ... */ } /* if */ break; - case iPROXY: - // Ignore usage on proxies. + case iMETHODMAP: + // Ignore usage on methodmaps. break; default: /* a variable */ diff --git a/sourcepawn/compiler/sc2.cpp b/sourcepawn/compiler/sc2.cpp index c660b6fb..b8cb9187 100644 --- a/sourcepawn/compiler/sc2.cpp +++ b/sourcepawn/compiler/sc2.cpp @@ -2924,25 +2924,6 @@ void delete_symbols(symbol *root,int level,int delete_labels,int delete_function constvalue *stateptr; int mustdelete; - // Hack - proxies have a "target" pointer, but the target could be deleted - // already if done inside the main loop below. To get around this we do a - // precursor pass. Note that proxies can only be at the global scope. - if (origRoot == &glbtab) { - symbol *iter = root; - while (iter->next) { - sym = iter->next; - - if (sym->ident != iPROXY) { - iter = sym; - continue; - } - - RemoveFromHashTable(sp_Globals, sym); - iter->next = sym->next; - free_symbol(sym); - } - } - /* erase only the symbols with a deeper nesting level than the * specified nesting level */ while (root->next!=NULL) { @@ -2985,9 +2966,12 @@ void delete_symbols(symbol *root,int level,int delete_labels,int delete_function mustdelete=delete_functions || (sym->usage & uNATIVE)!=0; assert(sym->parent==NULL); break; - case iPROXY: - // Original loop determined it was okay to keep. - mustdelete=FALSE; + case iMETHODMAP: + // We delete methodmap symbols at the end, but since methodmaps + // themselves get wiped, we null the pointer. + sym->methodmap = nullptr; + mustdelete = delete_functions; + assert(!sym->parent); break; case iARRAYCELL: case iARRAYCHAR: @@ -3143,7 +3127,7 @@ void markusage(symbol *sym,int usage) * * Returns a pointer to the global symbol (if found) or NULL (if not found) */ -symbol *findglb(const char *name, int filter, symbol **alias) +symbol *findglb(const char *name, int filter) { /* find a symbol with a matching automaton first */ symbol *sym=NULL; @@ -3168,12 +3152,6 @@ symbol *findglb(const char *name, int filter, symbol **alias) if (sym==NULL) sym=FindInHashTable(sp_Globals,name,fcurrent); - if (sym && sym->ident == iPROXY) { - if (alias) - *alias = sym; - return sym->target; - } - return sym; } diff --git a/sourcepawn/compiler/sc3.cpp b/sourcepawn/compiler/sc3.cpp index ca163ada..e7c5dd5b 100644 --- a/sourcepawn/compiler/sc3.cpp +++ b/sourcepawn/compiler/sc3.cpp @@ -2001,6 +2001,96 @@ static int hier2(value *lval) } /* switch */ } +static symbol * +fake_function_for_method(methodmap_t *map, const char *lexstr) +{ + // Fetch a fake function so errors aren't as crazy. + char tmpname[METHOD_NAMEMAX + 1]; + strcpy(tmpname, map->name); + strcat(tmpname, "."); + strcat(tmpname, lexstr); + tmpname[sNAMEMAX] = '\0'; + return fetchfunc(tmpname); +} + +enum FieldExprResult +{ + FER_Fail, + FER_Accessor, + FER_CallFunction, + FER_CallMethod +}; + +static FieldExprResult +field_expression(svalue &thisval, value *lval, symbol **target) +{ + // Catch invalid calls early so we don't compile with a tag mismatch. + switch (thisval.val.ident) { + case iARRAY: + case iREFARRAY: + error(106); + break; + + case iFUNCTN: + case iREFFUNC: + error(107); + break; + } + + cell lexval; + char *lexstr; + if (!needtoken(tSYMBOL)) + return FER_Fail; + tokeninfo(&lexval, &lexstr); + + if (thisval.val.ident == iMETHODMAP) { + methodmap_t *map = thisval.val.sym->methodmap; + methodmap_method_t *method = methodmap_find_method(map, lexstr); + if (!method) { + error(105, map->name, lexstr); + *target = fake_function_for_method(map, lexstr); + return FER_CallFunction; + } + + if (!method->is_static) + error(176, method->name, map->name); + *target = method->target; + return FER_CallFunction; + } + + methodmap_t *map; + if ((map = methodmap_find_by_tag(thisval.val.tag)) == NULL) { + error(104, pc_tagname(thisval.val.tag)); + return FER_Fail; + } + + methodmap_method_t *method; + if ((method = methodmap_find_method(map, lexstr)) == NULL) { + error(105, map->name, lexstr); + *target = fake_function_for_method(map, lexstr); + return FER_CallFunction; + } + + if (method && (method->getter || method->setter)) { + if (thisval.lvalue) + rvalue(lval); + clear_value(lval); + lval->ident = iACCESSOR; + lval->tag = method->property_tag(); + lval->accessor = method; + return FER_Accessor; + } + + *target = method->target; + + if (method->is_static) { + error(177, method->name, map->name, method->name); + return FER_CallFunction; + } + return FER_CallMethod; +} + + /* hier1 * * The highest hierarchy level: it looks for pointer and array indices @@ -2025,14 +2115,27 @@ static int hier1(value *lval1) lvalue=primary(lval1); symtok=tokeninfo(&val,&st); /* get token read by primary() */ cursym=lval1->sym; + restart: sym=cursym; + + if (lval1->ident == iMETHODMAP && + !(lexpeek('.') || lexpeek('('))) + { + // Cannot use methodmap as an rvalue/lvalue. + error(174, sym ? sym->name : "(unknown)"); + + lval1->ident = iCONSTEXPR; + lval1->tag = 0; + lval1->constval = 0; + } + if (matchtoken('[') || matchtoken('{') || matchtoken('(') || matchtoken('.')) { + tok=tokeninfo(&val,&st); /* get token read by matchtoken() */ if (lvalue && lval1->ident == iACCESSOR) { rvalue(lval1); lvalue = FALSE; } - tok=tokeninfo(&val,&st); /* get token read by matchtoken() */ magic_string = (sym && (sym->tag == pc_tag_string && sym->dim.array.level == 0)); if (sym==NULL && symtok!=tSYMBOL) { /* we do not have a valid symbol and we appear not to have read a valid @@ -2210,59 +2313,17 @@ restart: svalue *implicitthis = NULL; if (tok == '.') { - methodmap_t *map; - - /* Catch invalid calls early so we don't compile with a tag mismatch. */ - switch (thisval.val.ident) { - case iARRAY: - case iREFARRAY: - error(106); + switch (field_expression(thisval, lval1, &sym)) { + case FER_Fail: + case FER_CallFunction: break; - - case iFUNCTN: - case iREFFUNC: - error(107); - break; - } - - if ((map = methodmap_find_by_tag(thisval.val.tag)) == NULL) { - error(104, pc_tagname(thisval.val.tag)); - } - - if (needtoken(tSYMBOL) && map) { - cell lexval; - char *lexstr; - methodmap_method_t *method; - - tokeninfo(&lexval, &lexstr); - if ((method = methodmap_find_method(map, lexstr)) == NULL) - error(105, map->name, lexstr); - - if (method && (method->getter || method->setter)) { - if (lvalue) - rvalue(lval1); - clear_value(lval1); - lval1->ident = iACCESSOR; - lval1->tag = method->property_tag(); - lval1->accessor = method; - lvalue = TRUE; - goto restart; - } - - if (!method || !method->target) { - error(105, map->name, lexstr); - - // Fetch a fake function so errors aren't as crazy. - char tmpname[METHOD_NAMEMAX + 1]; - strcpy(tmpname, map->name); - strcat(tmpname, "."); - strcat(tmpname, lexstr); - tmpname[sNAMEMAX] = '\0'; - sym = fetchfunc(tmpname); - } else { + case FER_CallMethod: implicitthis = &thisval; - sym = method->target; - } + break; + case FER_Accessor: + goto restart; + default: + assert(false); } // If we don't find a '(' next, just fail to compile for now -- and @@ -2275,7 +2336,18 @@ restart: assert(tok=='('); if (sym==NULL || (sym->ident!=iFUNCTN && sym->ident!=iREFFUNC)) { - if (sym==NULL && sc_status==statFIRST) { + if (sym && sym->ident == iMETHODMAP && sym->methodmap) { + if (!sym->methodmap->ctor) { + // Immediately fatal - no function to call. + return error(172, sym->name); + } + if (sym->methodmap->nullable) { + // Keep going, this is basically a style thing. + error(170, sym->methodmap->name); + } + + sym = sym->methodmap->ctor->target; + } else if (sym==NULL && sc_status==statFIRST) { /* could be a "use before declaration"; in that case, create a stub * function so that the usage can be marked. */ @@ -2285,27 +2357,13 @@ restart: markusage(sym,uREAD); } else { return error(12); /* invalid function call */ - } /* if */ + } } else if ((sym->usage & uMISSING)!=0) { char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ funcdisplayname(symname,sym->name); error(4,symname); /* function not defined */ } /* if */ - // Check whether we're calling a constructor. This is a bit hacky, since - // we're relying on whatever the lval state is. - if ((sym->flags & flgPROXIED) && - lval1->proxy && - lval1->proxy->target == sym) - { - // Only constructors should be proxied, but we check anyway. - assert(!implicitthis); - if (methodmap_t *methodmap = methodmap_find_by_tag(sym->tag)) { - if (sym == methodmap->ctor->target && methodmap->nullable) - error(170, methodmap->name); - } - } - callfunction(sym,implicitthis,lval1,TRUE); if (lexpeek('.')) { lvalue = FALSE; @@ -2342,7 +2400,6 @@ restart: funcenum_t *fe = funcenum_for_symbol(target); lval1->sym = NULL; - lval1->proxy = NULL; lval1->ident = iCONSTEXPR; lval1->constval = (public_index << 1) | 1; lval1->tag = fe->tag; @@ -2429,8 +2486,7 @@ static int primary(value *lval) } /* if */ } /* if */ /* now try a global variable */ - symbol *alias = NULL; - if ((sym = findglb(st, sSTATEVAR, &alias)) != 0) { + if ((sym = findglb(st, sSTATEVAR)) != 0) { if (sym->ident==iFUNCTN || sym->ident==iREFFUNC) { /* if the function is only in the table because it was inserted as a * stub in the first pass (i.e. it was "used" but never declared or @@ -2442,15 +2498,18 @@ static int primary(value *lval) if ((sym->usage & uDEFINE)==0) error(17,st); lval->sym=sym; - lval->proxy=alias; lval->ident=sym->ident; lval->tag=sym->tag; - if (sym->ident==iARRAY || sym->ident==iREFARRAY) { - address(sym,sPRI); /* get starting address in primary register */ - return FALSE; /* return 0 for array (not lvalue) */ - } else { - return TRUE; /* return 1 if lvalue (not function or array) */ - } /* if */ + switch (sym->ident) { + case iARRAY: + case iREFARRAY: + address(sym,sPRI); /* get starting address in primary register */ + return FALSE; /* return 0 for array (not lvalue) */ + case iMETHODMAP: + return FALSE; + default: + return TRUE; /* return 1 if lvalue (not function or array) */ + } /* switch */ } /* if */ } else { if (!sc_allowproccall) @@ -2466,7 +2525,6 @@ static int primary(value *lval) assert(sym!=NULL); assert(sym->ident==iFUNCTN || sym->ident==iREFFUNC); lval->sym=sym; - lval->proxy=alias; lval->ident=sym->ident; lval->tag=sym->tag; return FALSE; /* return 0 for function (not an lvalue) */ @@ -2482,7 +2540,6 @@ static int primary(value *lval) static void clear_value(value *lval) { lval->sym=NULL; - lval->proxy=NULL; lval->constval=0L; lval->tag=0; lval->ident=0; diff --git a/sourcepawn/compiler/sc5-in.scp b/sourcepawn/compiler/sc5-in.scp index f9b3f0e0..7968cf5b 100644 --- a/sourcepawn/compiler/sc5-in.scp +++ b/sourcepawn/compiler/sc5-in.scp @@ -156,7 +156,7 @@ static const char *errmsg[] = { /*110*/ "%s has already been defined (previously seen as %s)\n", /*111*/ "expected identifier - did you forget a type?\n", /*112*/ "constructor function must return tag %s\n", -/*113*/ "cannot define constructor for \"%s\"; already exists as a %s\n", +/*113*/ "constructor for \"%s\" already exists\n", /*114*/ "missing type, or %s must have the same name as %s \"%s\"\n", /*115*/ "cannot use delete, %s %s has no destructor\n", /*116*/ "no methodmap or class was found for %s\n", @@ -217,6 +217,10 @@ static const char *errmsg[] = { /*171*/ "cannot use 'new' with non-object-like methodmap '%s'\n", /*172*/ "methodmap '%s' does not have a constructor\n", /*173*/ "'%s' is a newly reserved keyword that may be used in the future; use a different name as an identifier\n", +/*174*/ "symbol '%s' is a type and cannot be used as a value\n", +/*175*/ "constructors cannot be static\n", +/*176*/ "non-static method or property '%s' must be called with a value of type '%s'\n", +/*177*/ "static method '%s' must be invoked via its type (try '%s.%s')\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/sctracker.h b/sourcepawn/compiler/sctracker.h index 0e45cbfa..27f44ecc 100644 --- a/sourcepawn/compiler/sctracker.h +++ b/sourcepawn/compiler/sctracker.h @@ -85,6 +85,7 @@ typedef struct methodmap_method_s symbol *target; symbol *getter; symbol *setter; + bool is_static; int property_tag() const { assert(getter || setter); @@ -100,10 +101,10 @@ typedef struct methodmap_method_s } } methodmap_method_t; -typedef struct methodmap_s +struct methodmap_t { - struct methodmap_s *next; - struct methodmap_s *parent; + methodmap_t *next; + methodmap_t *parent; int tag; int nullable; LayoutSpec spec; @@ -114,7 +115,7 @@ typedef struct methodmap_s // Shortcut. methodmap_method_t *dtor; methodmap_method_t *ctor; -} methodmap_t; +}; /** * Pawn Structs diff --git a/sourcepawn/compiler/tests/fail-bad-arg-decls.txt b/sourcepawn/compiler/tests/fail-bad-arg-decls.txt index d92248fe..f6404967 100644 --- a/sourcepawn/compiler/tests/fail-bad-arg-decls.txt +++ b/sourcepawn/compiler/tests/fail-bad-arg-decls.txt @@ -1,3 +1,3 @@ (1) : error 001: expected token: "-identifier-", but found ")" (5) : error 140: new-style array types cannot specify dimension sizes as part of their type -(9) : error 001: expected token: "-identifier-", but found ":" +(9) : warning 238: 'int:' is an illegal cast; use view_as(expression) diff --git a/sourcepawn/compiler/tests/fail-call-non-static-on-type.sp b/sourcepawn/compiler/tests/fail-call-non-static-on-type.sp new file mode 100644 index 00000000..ac6df284 --- /dev/null +++ b/sourcepawn/compiler/tests/fail-call-non-static-on-type.sp @@ -0,0 +1,12 @@ +native printnum(t); + +methodmap X +{ + public int GetThing() { + return 10; + } +} + +public main() { + printnum(X.GetThing()); +} diff --git a/sourcepawn/compiler/tests/fail-call-non-static-on-type.txt b/sourcepawn/compiler/tests/fail-call-non-static-on-type.txt new file mode 100644 index 00000000..42c312db --- /dev/null +++ b/sourcepawn/compiler/tests/fail-call-non-static-on-type.txt @@ -0,0 +1 @@ +(11) : error 176: non-static method or property 'GetThing' must be called with a value of type 'X' diff --git a/sourcepawn/compiler/tests/fail-call-static-via-instance.sp b/sourcepawn/compiler/tests/fail-call-static-via-instance.sp new file mode 100644 index 00000000..24a16591 --- /dev/null +++ b/sourcepawn/compiler/tests/fail-call-static-via-instance.sp @@ -0,0 +1,13 @@ +native printnum(t); + +methodmap X +{ + public static int GetThing() { + return 10; + } +} + +public main() { + X x; + printnum(x.GetThing()); +} diff --git a/sourcepawn/compiler/tests/fail-call-static-via-instance.txt b/sourcepawn/compiler/tests/fail-call-static-via-instance.txt new file mode 100644 index 00000000..9a7dbc95 --- /dev/null +++ b/sourcepawn/compiler/tests/fail-call-static-via-instance.txt @@ -0,0 +1 @@ +(12) : error 177: static method 'GetThing' must be invoked via its type (try 'X.GetThing') diff --git a/sourcepawn/compiler/tests/fail-methodmap-as-rvalue.sp b/sourcepawn/compiler/tests/fail-methodmap-as-rvalue.sp new file mode 100644 index 00000000..1218fca6 --- /dev/null +++ b/sourcepawn/compiler/tests/fail-methodmap-as-rvalue.sp @@ -0,0 +1,17 @@ +native printnum(x); + +enum X { +}; + +methodmap X { + public X(int y) { + printnum(y); + return view_as(0); + } +}; + +public main() +{ + X x = X(5); + return X; +} diff --git a/sourcepawn/compiler/tests/fail-methodmap-as-rvalue.txt b/sourcepawn/compiler/tests/fail-methodmap-as-rvalue.txt new file mode 100644 index 00000000..7dfa5277 --- /dev/null +++ b/sourcepawn/compiler/tests/fail-methodmap-as-rvalue.txt @@ -0,0 +1 @@ +(16) : error 174: symbol 'X' is a type and cannot be used as a value diff --git a/sourcepawn/compiler/tests/ok-call-static-method.sp b/sourcepawn/compiler/tests/ok-call-static-method.sp new file mode 100644 index 00000000..19a887ea --- /dev/null +++ b/sourcepawn/compiler/tests/ok-call-static-method.sp @@ -0,0 +1,12 @@ +native printnum(t); + +methodmap X +{ + public static int GetThing() { + return 10; + } +} + +public main() { + printnum(X.GetThing()); +}