Add static method support to methodmaps.
This commit is contained in:
parent
afeae84340
commit
5b69efe5d4
@ -3625,7 +3625,13 @@ static void make_primitive(typeinfo_t *type, int tag)
|
|||||||
type->ident = iVARIABLE;
|
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;
|
declinfo_t decl;
|
||||||
memset(&decl, 0, sizeof(decl));
|
memset(&decl, 0, sizeof(decl));
|
||||||
@ -3640,7 +3646,7 @@ symbol *parse_inline_function(methodmap_t *map, const typeinfo_t *type, const ch
|
|||||||
decl.type.is_new = TRUE;
|
decl.type.is_new = TRUE;
|
||||||
|
|
||||||
const int *thistag = NULL;
|
const int *thistag = NULL;
|
||||||
if (!is_ctor)
|
if (!is_ctor && !is_static)
|
||||||
thistag = &map->tag;
|
thistag = &map->tag;
|
||||||
|
|
||||||
// Build a new symbol. Construct a temporary name including the class.
|
// Build a new symbol. Construct a temporary name including the class.
|
||||||
@ -3752,7 +3758,7 @@ int parse_property_accessor(const typeinfo_t *type, methodmap_t *map, methodmap_
|
|||||||
ret_type = &voidtype;
|
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)
|
if (!target)
|
||||||
@ -3851,8 +3857,12 @@ methodmap_method_t *parse_method(methodmap_t *map)
|
|||||||
int is_dtor = 0;
|
int is_dtor = 0;
|
||||||
int is_bind = 0;
|
int is_bind = 0;
|
||||||
int is_native = 0;
|
int is_native = 0;
|
||||||
|
bool is_static = false;
|
||||||
const char *spectype = layout_spec_name(map->spec);
|
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 ~).
|
// This stores the name of the method (for destructors, we add a ~).
|
||||||
token_ident_t ident;
|
token_ident_t ident;
|
||||||
strcpy(ident.name, "__unknown__");
|
strcpy(ident.name, "__unknown__");
|
||||||
@ -3864,7 +3874,8 @@ methodmap_method_t *parse_method(methodmap_t *map)
|
|||||||
typeinfo_t type;
|
typeinfo_t type;
|
||||||
memset(&type, 0, sizeof(type));
|
memset(&type, 0, sizeof(type));
|
||||||
|
|
||||||
if (matchtoken('~')) {
|
// Destructors cannot be static.
|
||||||
|
if (!is_static && matchtoken('~')) {
|
||||||
// We got something like "public ~Blah = X"
|
// We got something like "public ~Blah = X"
|
||||||
is_bind = TRUE;
|
is_bind = TRUE;
|
||||||
is_dtor = TRUE;
|
is_dtor = TRUE;
|
||||||
@ -3951,6 +3962,11 @@ methodmap_method_t *parse_method(methodmap_t *map)
|
|||||||
error(114, "constructor", spectype, map->name);
|
error(114, "constructor", spectype, map->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_ctor && is_static) {
|
||||||
|
// Constructors may not be static.
|
||||||
|
error(175);
|
||||||
|
}
|
||||||
|
|
||||||
symbol *target = NULL;
|
symbol *target = NULL;
|
||||||
if (is_bind) {
|
if (is_bind) {
|
||||||
// Find an existing symbol.
|
// Find an existing symbol.
|
||||||
@ -3960,7 +3976,7 @@ methodmap_method_t *parse_method(methodmap_t *map)
|
|||||||
else if (target->ident != iFUNCTN)
|
else if (target->ident != iFUNCTN)
|
||||||
error(10);
|
error(10);
|
||||||
} else {
|
} 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)
|
if (!target)
|
||||||
@ -3998,12 +4014,14 @@ methodmap_method_t *parse_method(methodmap_t *map)
|
|||||||
method->target = target;
|
method->target = target;
|
||||||
method->getter = NULL;
|
method->getter = NULL;
|
||||||
method->setter = NULL;
|
method->setter = NULL;
|
||||||
|
method->is_static = is_static;
|
||||||
|
|
||||||
// If the symbol is a constructor, we bypass the initial argument checks.
|
// If the symbol is a constructor, we bypass the initial argument checks.
|
||||||
if (is_ctor) {
|
if (is_ctor) {
|
||||||
if (map->ctor)
|
if (map->ctor)
|
||||||
error(113, map->name);
|
error(113, map->name);
|
||||||
} else if (!check_this_tag(map, target)) {
|
} else if (!is_static) {
|
||||||
|
if (!check_this_tag(map, target))
|
||||||
error(108, spectype, map->name);
|
error(108, spectype, map->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2001,6 +2001,96 @@ static int hier2(value *lval)
|
|||||||
} /* switch */
|
} /* 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
|
/* hier1
|
||||||
*
|
*
|
||||||
* The highest hierarchy level: it looks for pointer and array indices
|
* The highest hierarchy level: it looks for pointer and array indices
|
||||||
@ -2223,59 +2313,17 @@ restart:
|
|||||||
|
|
||||||
svalue *implicitthis = NULL;
|
svalue *implicitthis = NULL;
|
||||||
if (tok == '.') {
|
if (tok == '.') {
|
||||||
methodmap_t *map;
|
switch (field_expression(thisval, lval1, &sym)) {
|
||||||
|
case FER_Fail:
|
||||||
/* Catch invalid calls early so we don't compile with a tag mismatch. */
|
case FER_CallFunction:
|
||||||
switch (thisval.val.ident) {
|
|
||||||
case iARRAY:
|
|
||||||
case iREFARRAY:
|
|
||||||
error(106);
|
|
||||||
break;
|
break;
|
||||||
|
case FER_CallMethod:
|
||||||
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 {
|
|
||||||
implicitthis = &thisval;
|
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
|
// If we don't find a '(' next, just fail to compile for now -- and
|
||||||
|
@ -218,6 +218,9 @@ static const char *errmsg[] = {
|
|||||||
/*172*/ "methodmap '%s' does not have a constructor\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",
|
/*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",
|
/*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
|
#else
|
||||||
"\315e\306\227\266k\217:\235\277bu\201fo\220\204\223\012",
|
"\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",
|
"\202l\224\250s\205g\346\356e\233\201(\243\315\214\267\202) \253 f\255low ea\305 \042c\353e\042\012",
|
||||||
|
@ -85,6 +85,7 @@ typedef struct methodmap_method_s
|
|||||||
symbol *target;
|
symbol *target;
|
||||||
symbol *getter;
|
symbol *getter;
|
||||||
symbol *setter;
|
symbol *setter;
|
||||||
|
bool is_static;
|
||||||
|
|
||||||
int property_tag() const {
|
int property_tag() const {
|
||||||
assert(getter || setter);
|
assert(getter || setter);
|
||||||
|
12
sourcepawn/compiler/tests/fail-call-non-static-on-type.sp
Normal file
12
sourcepawn/compiler/tests/fail-call-non-static-on-type.sp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
native printnum(t);
|
||||||
|
|
||||||
|
methodmap X
|
||||||
|
{
|
||||||
|
public int GetThing() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public main() {
|
||||||
|
printnum(X.GetThing());
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
(11) : error 176: non-static method or property 'GetThing' must be called with a value of type 'X'
|
13
sourcepawn/compiler/tests/fail-call-static-via-instance.sp
Normal file
13
sourcepawn/compiler/tests/fail-call-static-via-instance.sp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
native printnum(t);
|
||||||
|
|
||||||
|
methodmap X
|
||||||
|
{
|
||||||
|
public static int GetThing() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public main() {
|
||||||
|
X x;
|
||||||
|
printnum(x.GetThing());
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
(12) : error 177: static method 'GetThing' must be invoked via its type (try 'X.GetThing')
|
12
sourcepawn/compiler/tests/ok-call-static-method.sp
Normal file
12
sourcepawn/compiler/tests/ok-call-static-method.sp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
native printnum(t);
|
||||||
|
|
||||||
|
methodmap X
|
||||||
|
{
|
||||||
|
public static int GetThing() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public main() {
|
||||||
|
printnum(X.GetThing());
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user