From 9f5c8b60ae25cc0a40f51acafbd1c7c5f48a011c Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sun, 30 Nov 2014 18:38:26 -0800 Subject: [PATCH] Add a "new" keyword for constructing nullable methodmaps. --- sourcepawn/compiler/sc.h | 5 ++ sourcepawn/compiler/sc1.cpp | 3 ++ sourcepawn/compiler/sc2.cpp | 5 +- sourcepawn/compiler/sc3.cpp | 51 +++++++++++++++++++ sourcepawn/compiler/sc5-in.scp | 3 ++ sourcepawn/compiler/sctracker.h | 1 + .../tests/fail-new-with-no-constructor.sp | 10 ++++ .../tests/fail-new-with-no-constructor.txt | 1 + .../tests/fail-new-with-no-methodmap.sp | 13 +++++ .../tests/fail-new-with-no-methodmap.txt | 1 + .../tests/fail-new-with-non-nullable.sp | 11 ++++ .../tests/fail-new-with-non-nullable.txt | 1 + .../compiler/tests/fail-nullable-needs-new.sp | 11 ++++ .../tests/fail-nullable-needs-new.txt | 1 + .../tests/ok-new-with-nullable-methodmap.sp | 15 ++++++ 15 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 sourcepawn/compiler/tests/fail-new-with-no-constructor.sp create mode 100644 sourcepawn/compiler/tests/fail-new-with-no-constructor.txt create mode 100644 sourcepawn/compiler/tests/fail-new-with-no-methodmap.sp create mode 100644 sourcepawn/compiler/tests/fail-new-with-no-methodmap.txt create mode 100644 sourcepawn/compiler/tests/fail-new-with-non-nullable.sp create mode 100644 sourcepawn/compiler/tests/fail-new-with-non-nullable.txt create mode 100644 sourcepawn/compiler/tests/fail-nullable-needs-new.sp create mode 100644 sourcepawn/compiler/tests/fail-nullable-needs-new.txt create mode 100644 sourcepawn/compiler/tests/ok-new-with-nullable-methodmap.sp diff --git a/sourcepawn/compiler/sc.h b/sourcepawn/compiler/sc.h index 9e200699..e98e7cfc 100644 --- a/sourcepawn/compiler/sc.h +++ b/sourcepawn/compiler/sc.h @@ -227,6 +227,7 @@ 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 */ @@ -396,6 +397,7 @@ enum TokenKind { tBEGIN, tBREAK, tCASE, + tCAST_TO, tCELLSOF, tCHAR, tCONST, @@ -417,6 +419,7 @@ enum TokenKind { tGOTO, tIF, tINT, + tLET, tMETHODMAP, tNATIVE, tNEW, @@ -437,6 +440,8 @@ enum TokenKind { tTHIS, tTYPEDEF, tUNION, + tVAR, + tVIEW_AS, tVOID, tWHILE, /* compiler directives */ diff --git a/sourcepawn/compiler/sc1.cpp b/sourcepawn/compiler/sc1.cpp index f362b86b..c2be2ccb 100644 --- a/sourcepawn/compiler/sc1.cpp +++ b/sourcepawn/compiler/sc1.cpp @@ -3594,6 +3594,7 @@ static void define_constructor(methodmap_t *map, methodmap_method_t *method) 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 @@ -4034,6 +4035,8 @@ methodmap_method_t *parse_method(methodmap_t *map) if (is_dtor) map->dtor = method; + if (is_ctor) + map->ctor = method; require_newline(is_bind || (target->usage & uNATIVE)); return method; diff --git a/sourcepawn/compiler/sc2.cpp b/sourcepawn/compiler/sc2.cpp index c0119e24..f4d83b9d 100644 --- a/sourcepawn/compiler/sc2.cpp +++ b/sourcepawn/compiler/sc2.cpp @@ -1961,12 +1961,13 @@ const char *sc_tokens[] = { "...", "..", "::", "assert", "*begin", "break", - "case", "cellsof", "char", "const", "continue", + "case", "cast_to", "cellsof", "char", "const", "continue", "decl", "default", "defined", "delete", "do", "else", "*end", "enum", "exit", "for", "forward", "funcenum", "functag", "function", "goto", "if", "int", + "let", "methodmap", "native", "new", "null", "__nullable__", "object", "operator", @@ -1975,7 +1976,7 @@ const char *sc_tokens[] = { "sizeof", "sleep", "static", "stock", "struct", "switch", "tagof", "*then", "this", "typedef", "union", - "void", + "var", "view_as", "void", "while", "#assert", "#define", "#else", "#elseif", "#emit", "#endif", "#endinput", "#endscript", "#error", "#file", "#if", "#include", "#line", "#pragma", diff --git a/sourcepawn/compiler/sc3.cpp b/sourcepawn/compiler/sc3.cpp index 5fe2878c..240739ec 100644 --- a/sourcepawn/compiler/sc3.cpp +++ b/sourcepawn/compiler/sc3.cpp @@ -1619,6 +1619,47 @@ static int hier2(value *lval) lval->constval=-lval->constval; } /* if */ return FALSE; + case tNEW: /* call nullable methodmap constructor */ + { + tok = lex(&val, &st); + if (tok != tSYMBOL) + return error(20, st); /* illegal symbol name */ + + symbol *target = NULL; + methodmap_t *methodmap = methodmap_find_by_name(st); + if (!methodmap) + error(116, st); + else if (!methodmap->nullable) + error(171, methodmap->name); + else if (!methodmap->ctor) + error(172, methodmap->name); + else + target = methodmap->ctor->target; + + if (!target) { + needtoken('('); + int depth = 1; + // Eat tokens until we get a newline or EOF or ')' or ';' + while (true) { + if (peek_same_line() == tEOL) + return FALSE; + if ((tok = lex(&val, &st)) == 0) + return FALSE; + if (tok == ')') { + if (--depth == 0) + return FALSE; + } + if (tok == ';') + return FALSE; + if (tok == '(') + depth++; + } + } + + needtoken('('); + callfunction(target, NULL, lval, TRUE); + return FALSE; + } case tLABEL: /* tagname override */ tag=pc_addtag(st); lval->cmptag=tag; @@ -2223,6 +2264,16 @@ restart: funcdisplayname(symname,sym->name); error(4,symname); /* function not defined */ } /* if */ + + if (sym->flags & flgPROXIED) { + // 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; diff --git a/sourcepawn/compiler/sc5-in.scp b/sourcepawn/compiler/sc5-in.scp index f9bd8f8d..111b0f7d 100644 --- a/sourcepawn/compiler/sc5-in.scp +++ b/sourcepawn/compiler/sc5-in.scp @@ -213,6 +213,9 @@ static const char *errmsg[] = { /*167*/ "cannot use delete, %s do not have destructors\n", /*168*/ "re-tagging enums is no longer supported\n", /*169*/ "cannot tag an enum as implicit-int\n", +/*170*/ "creating new object '%s' requires using 'new' before its constructor\n", +/*171*/ "cannot use 'new' with non-object-like methodmap '%s'\n", +/*172*/ "methodmap '%s' does not have a constructor\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 997a99dc..0e45cbfa 100644 --- a/sourcepawn/compiler/sctracker.h +++ b/sourcepawn/compiler/sctracker.h @@ -113,6 +113,7 @@ typedef struct methodmap_s // Shortcut. methodmap_method_t *dtor; + methodmap_method_t *ctor; } methodmap_t; /** diff --git a/sourcepawn/compiler/tests/fail-new-with-no-constructor.sp b/sourcepawn/compiler/tests/fail-new-with-no-constructor.sp new file mode 100644 index 00000000..51580179 --- /dev/null +++ b/sourcepawn/compiler/tests/fail-new-with-no-constructor.sp @@ -0,0 +1,10 @@ +methodmap Handle __nullable__ +{ + public native ~Handle(); +}; + +public t() +{ + Handle egg = new Handle(); + delete egg; +} diff --git a/sourcepawn/compiler/tests/fail-new-with-no-constructor.txt b/sourcepawn/compiler/tests/fail-new-with-no-constructor.txt new file mode 100644 index 00000000..d0ac818f --- /dev/null +++ b/sourcepawn/compiler/tests/fail-new-with-no-constructor.txt @@ -0,0 +1 @@ +(8) : error 172: methodmap 'Handle' does not have a constructor diff --git a/sourcepawn/compiler/tests/fail-new-with-no-methodmap.sp b/sourcepawn/compiler/tests/fail-new-with-no-methodmap.sp new file mode 100644 index 00000000..6d666f44 --- /dev/null +++ b/sourcepawn/compiler/tests/fail-new-with-no-methodmap.sp @@ -0,0 +1,13 @@ +methodmap Handle __nullable__ +{ + public native Handle(); + public native ~Handle(); +}; + +enum Crab {}; + +public t() +{ + Crab egg = new Crab(); + delete egg; +} diff --git a/sourcepawn/compiler/tests/fail-new-with-no-methodmap.txt b/sourcepawn/compiler/tests/fail-new-with-no-methodmap.txt new file mode 100644 index 00000000..27d51f00 --- /dev/null +++ b/sourcepawn/compiler/tests/fail-new-with-no-methodmap.txt @@ -0,0 +1 @@ +(11) : error 116: no methodmap or class was found for Crab diff --git a/sourcepawn/compiler/tests/fail-new-with-non-nullable.sp b/sourcepawn/compiler/tests/fail-new-with-non-nullable.sp new file mode 100644 index 00000000..95684f5d --- /dev/null +++ b/sourcepawn/compiler/tests/fail-new-with-non-nullable.sp @@ -0,0 +1,11 @@ +methodmap Handle +{ + public native Handle(); + public native ~Handle(); +}; + +public t() +{ + Handle egg = new Handle(); + delete egg; +} diff --git a/sourcepawn/compiler/tests/fail-new-with-non-nullable.txt b/sourcepawn/compiler/tests/fail-new-with-non-nullable.txt new file mode 100644 index 00000000..812014e4 --- /dev/null +++ b/sourcepawn/compiler/tests/fail-new-with-non-nullable.txt @@ -0,0 +1 @@ +(9) : error 171: cannot use 'new' with non-object-like methodmap 'Handle' diff --git a/sourcepawn/compiler/tests/fail-nullable-needs-new.sp b/sourcepawn/compiler/tests/fail-nullable-needs-new.sp new file mode 100644 index 00000000..6c09a80a --- /dev/null +++ b/sourcepawn/compiler/tests/fail-nullable-needs-new.sp @@ -0,0 +1,11 @@ +methodmap Handle __nullable__ +{ + public native Handle(); + public native ~Handle(); +}; + +public t() +{ + Handle egg = Handle(); + delete egg; +} diff --git a/sourcepawn/compiler/tests/fail-nullable-needs-new.txt b/sourcepawn/compiler/tests/fail-nullable-needs-new.txt new file mode 100644 index 00000000..701fb593 --- /dev/null +++ b/sourcepawn/compiler/tests/fail-nullable-needs-new.txt @@ -0,0 +1 @@ +(9) : error 170: creating new object 'Handle' requires using 'new' before its constructor diff --git a/sourcepawn/compiler/tests/ok-new-with-nullable-methodmap.sp b/sourcepawn/compiler/tests/ok-new-with-nullable-methodmap.sp new file mode 100644 index 00000000..700d5c9f --- /dev/null +++ b/sourcepawn/compiler/tests/ok-new-with-nullable-methodmap.sp @@ -0,0 +1,15 @@ +native void printnum(int num); + +methodmap Handle __nullable__ +{ + public Handle() { + return Handle:2; + } + public native ~Handle(); +}; + +public main() +{ + Handle egg = new Handle(); + printnum(_:egg); +}