From: Francis De Brabandere <francisdb@gmail.com> Native VBScript lexes Default as a keyword only when the next word is a member kind or access modifier, so class member variables can be named Default. A Default specification with a missing or non-Public modifier reports error 1057 inside a class body and a generic syntax error at global scope. --- dlls/vbscript/lex.c | 51 +++++++++++++++++++++++++ dlls/vbscript/parser.y | 2 + dlls/vbscript/tests/lang.vbs | 72 ++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+) diff --git a/dlls/vbscript/lex.c b/dlls/vbscript/lex.c index db1b8b7b355..74f882d9994 100644 --- a/dlls/vbscript/lex.c +++ b/dlls/vbscript/lex.c @@ -141,6 +141,52 @@ static int check_keyword(parser_ctx_t *ctx, const WCHAR *word, const WCHAR **lva return 0; } +/* Check whether the next word matches the given lowercase keyword using + ASCII-only case-insensitive matching, without advancing the parser. */ +static BOOL next_word_is(const WCHAR *ptr, const WCHAR *end, const WCHAR *word) +{ + WCHAR c; + + while(*word) { + if(ptr == end) + return FALSE; + c = *ptr; + if(c >= 'A' && c <= 'Z') c += 'a' - 'A'; + if(c != *word) + return FALSE; + ptr++; + word++; + } + + return ptr == end || !is_identifier_char(*ptr); +} + +/* 'default' is a keyword only in class member declarations, where it is + followed by another modifier or by the member kind; anywhere else it is + an ordinary identifier. */ +static BOOL is_default_keyword(parser_ctx_t *ctx) +{ + const WCHAR *ptr = ctx->ptr; + + for(;;) { + while(ptr < ctx->end && (*ptr == ' ' || *ptr == '\t')) + ptr++; + if(ptr == ctx->end || *ptr != '_') + break; + ptr++; + while(ptr < ctx->end && (*ptr == ' ' || *ptr == '\t')) + ptr++; + if(ptr == ctx->end || (*ptr != '\r' && *ptr != '\n')) + return FALSE; + while(ptr < ctx->end && (*ptr == '\r' || *ptr == '\n')) + ptr++; + } + + return next_word_is(ptr, ctx->end, L"sub") || next_word_is(ptr, ctx->end, L"function") + || next_word_is(ptr, ctx->end, L"property") || next_word_is(ptr, ctx->end, L"public") + || next_word_is(ptr, ctx->end, L"private"); +} + static int check_keywords(parser_ctx_t *ctx, const WCHAR **lval) { int min = 0, max = ARRAY_SIZE(keywords)-1, r, i; @@ -502,9 +548,14 @@ static int parse_next_token(void *lval, unsigned *loc, parser_ctx_t *ctx) return parse_numeric_literal(ctx, lval); if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + const WCHAR *word_start = ctx->ptr; int ret = 0; if(ctx->last_token != '.' && ctx->last_token != tDOT) ret = check_keywords(ctx, lval); + if(ret == tDEFAULT && !is_default_keyword(ctx)) { + ctx->ptr = word_start; + ret = 0; + } if(!ret) return parse_identifier(ctx, lval); if(ret != tREM) diff --git a/dlls/vbscript/parser.y b/dlls/vbscript/parser.y index fea607a8d63..4a79692a6ad 100644 --- a/dlls/vbscript/parser.y +++ b/dlls/vbscript/parser.y @@ -599,6 +599,7 @@ ClassBody | tDIM DimDeclList StSep ClassBody { $$ = add_dim_prop(ctx, $4, $2, 0); CHECK_ERROR; } | PropertyDecl { $$ = add_class_function(ctx, new_class_decl(ctx), $1); CHECK_ERROR; } | PropertyDecl StSep ClassBody { $$ = add_class_function(ctx, $3, $1); CHECK_ERROR; } + | tDEFAULT error { ctx->error_loc = @1; ctx->hres = MAKE_VBSERROR(VBSE_DEFAULT_MUST_BE_PUBLIC); YYABORT; } PropertyDecl : Storage_opt tPROPERTY tGET Identifier ArgumentsDecl_opt StSep BodyStatements tEND tPROPERTY @@ -650,6 +651,7 @@ Storage_opt Storage : tPUBLIC tDEFAULT { $$ = STORAGE_IS_DEFAULT; } | tDEFAULT tPRIVATE { ctx->error_loc = @1; ctx->hres = MAKE_VBSERROR(VBSE_DEFAULT_MUST_BE_PUBLIC); CHECK_ERROR; } + | tPRIVATE tDEFAULT { ctx->error_loc = @2; ctx->hres = MAKE_VBSERROR(VBSE_DEFAULT_MUST_BE_PUBLIC); CHECK_ERROR; } | tPUBLIC { $$ = 0; } | tPRIVATE { $$ = STORAGE_IS_PRIVATE; } diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index acdd992cbcf..e935d32b015 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -3826,6 +3826,78 @@ funcCalled = "" 'obj() 'call ok(funcCalled = "init","funcCalled=" & funcCalled) +class DefaultVarTest1 + Public Default +end class + +Set obj = New DefaultVarTest1 +obj.Default = 31 +call ok(obj.Default = 31, "obj.Default = " & obj.Default) + +class DefaultVarTest2 + Public Default(2), DefaultOther +end class + +Set obj = New DefaultVarTest2 +obj.Default(1) = 32 +obj.DefaultOther = 33 +call ok(obj.Default(1) = 32, "obj.Default(1) = " & obj.Default(1)) +call ok(obj.DefaultOther = 33, "obj.DefaultOther = " & obj.DefaultOther) + +class DefaultVarTest3 + Private Default + Public Sub SetIt(v) + Default = v + End Sub + Public Function GetIt + GetIt = Default + End Function +end class + +Set obj = New DefaultVarTest3 +obj.SetIt 34 +call ok(obj.GetIt = 34, "obj.GetIt = " & obj.GetIt) + +Dim Default +Default = 35 +call ok(Default = 35, "global Default = " & Default) + +class DefaultVarTest4 + Public Default _ + Function ContinuedDefault() + ContinuedDefault = 36 + End Function +end class + +Set obj = New DefaultVarTest4 +call ok(obj() = 36, "default function after line continuation = " & obj()) + +sub TestDefaultDeclErrors + on error resume next + + err.clear : Execute "Class CDE1 : Default Sub S : End Sub : End Class" + call ok(err.number = 1057, "Default Sub err=" & err.number) + + err.clear : Execute "Class CDE2 : Default Function F : End Function : End Class" + call ok(err.number = 1057, "Default Function err=" & err.number) + + err.clear : Execute "Class CDE3 : Default Property Get P : P = 1 : End Property : End Class" + call ok(err.number = 1057, "Default Property Get err=" & err.number) + + err.clear : Execute "Class CDE4 : Private Default Sub S : End Sub : End Class" + call ok(err.number = 1057, "Private Default Sub err=" & err.number) + + err.clear : Execute "Class CDE5 : Default Public Sub S : End Sub : End Class" + call ok(err.number = 1057, "Default Public Sub err=" & err.number) + + err.clear : Execute "Class CDE6 : Default Private Sub S : End Sub : End Class" + call ok(err.number = 1057, "Default Private Sub err=" & err.number) + + err.clear : Execute "Default Sub S : End Sub" + call ok(err.number = 1002, "global Default Sub err=" & err.number) +end sub +call TestDefaultDeclErrors + with nothing end with -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11124