From 5b69efe5d4a113c453b8706a65353363707b23e0 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 11 Dec 2014 23:18:32 -0800 Subject: [PATCH] Add static method support to methodmaps. --- sourcepawn/compiler/sc1.cpp | 32 +++- sourcepawn/compiler/sc3.cpp | 150 ++++++++++++------ sourcepawn/compiler/sc5-in.scp | 3 + sourcepawn/compiler/sctracker.h | 1 + .../tests/fail-call-non-static-on-type.sp | 12 ++ .../tests/fail-call-non-static-on-type.txt | 1 + .../tests/fail-call-static-via-instance.sp | 13 ++ .../tests/fail-call-static-via-instance.txt | 1 + .../compiler/tests/ok-call-static-method.sp | 12 ++ 9 files changed, 167 insertions(+), 58 deletions(-) create mode 100644 sourcepawn/compiler/tests/fail-call-non-static-on-type.sp create mode 100644 sourcepawn/compiler/tests/fail-call-non-static-on-type.txt create mode 100644 sourcepawn/compiler/tests/fail-call-static-via-instance.sp create mode 100644 sourcepawn/compiler/tests/fail-call-static-via-instance.txt create mode 100644 sourcepawn/compiler/tests/ok-call-static-method.sp diff --git a/sourcepawn/compiler/sc1.cpp b/sourcepawn/compiler/sc1.cpp index cc4fcc37..892cd32b 100644 --- a/sourcepawn/compiler/sc1.cpp +++ b/sourcepawn/compiler/sc1.cpp @@ -3625,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)); @@ -3640,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. @@ -3752,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) @@ -3851,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__"); @@ -3864,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; @@ -3951,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. @@ -3960,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) @@ -3998,13 +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) { if (map->ctor) error(113, map->name); - } else if (!check_this_tag(map, target)) { - error(108, spectype, map->name); + } else if (!is_static) { + if (!check_this_tag(map, target)) + error(108, spectype, map->name); } if (is_dtor) diff --git a/sourcepawn/compiler/sc3.cpp b/sourcepawn/compiler/sc3.cpp index 8a8c0314..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 @@ -2223,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 diff --git a/sourcepawn/compiler/sc5-in.scp b/sourcepawn/compiler/sc5-in.scp index a91e3928..7968cf5b 100644 --- a/sourcepawn/compiler/sc5-in.scp +++ b/sourcepawn/compiler/sc5-in.scp @@ -218,6 +218,9 @@ static const char *errmsg[] = { /*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 8ffee95d..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); 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/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()); +}