[PATCH v3 0/5] MR10405: Draft: vbscript: Return proper error for undefined variables with Option Explicit.
When Option Explicit is set and an undefined variable is used, return VBS error 500 (Variable is undefined) with the variable name in the description, matching Windows behavior. Previously Wine returned E_FAIL with no description. -- v3: vbscript: Return proper error for undefined variables with Option Explicit. vbscript: Improve some error codes in the parser. vbscript: Improve some error codes in the lexer. vbscript: Improve some error codes in the compiler. vbscript/tests: Add error code tests for compile errors. https://gitlab.winehq.org/wine/wine/-/merge_requests/10405
From: Francis De Brabandere <francisdb@gmail.com> Add error_code field to parse error tests, using negative values for codes that Wine does not yet return correctly (todo_wine), matching the existing pattern for error_char. --- dlls/vbscript/tests/run.c | 169 +++++++++++++++++++++++++++++++--- dlls/vbscript/vbscript.rc | 32 +++++++ dlls/vbscript/vbscript_defs.h | 32 +++++++ 3 files changed, 220 insertions(+), 13 deletions(-) diff --git a/dlls/vbscript/tests/run.c b/dlls/vbscript/tests/run.c index 80cd3a34bd2..a641214b038 100644 --- a/dlls/vbscript/tests/run.c +++ b/dlls/vbscript/tests/run.c @@ -2007,6 +2007,7 @@ static HRESULT WINAPI ActiveScriptSite_OnStateChange(IActiveScriptSite *iface, S static IActiveScriptError **store_script_error; static ULONG error_line; static LONG error_char; +static USHORT error_code; static HRESULT WINAPI ActiveScriptSite_OnScriptError(IActiveScriptSite *iface, IActiveScriptError *pscripterror) { @@ -2023,6 +2024,15 @@ static HRESULT WINAPI ActiveScriptSite_OnScriptError(IActiveScriptSite *iface, I trace("Error in line %lu: %x %s\n", error_line + 1, info.wCode, wine_dbgstr_w(info.bstrDescription)); }else { IDispatchEx *dispex; + EXCEPINFO info; + + hres = IActiveScriptError_GetExceptionInfo(pscripterror, &info); + if(SUCCEEDED(hres)) { + error_code = HRESULT_CODE(info.scode); + SysFreeString(info.bstrSource); + SysFreeString(info.bstrDescription); + SysFreeString(info.bstrHelpFile); + } hres = IActiveScriptError_QueryInterface(pscripterror, &IID_IDispatchEx, (void**)&dispex); ok(hres == E_NOINTERFACE, "QI(IDispatchEx) returned: %08lx\n", hres); @@ -2696,6 +2706,7 @@ static void test_parse_errors(void) const WCHAR *src; unsigned error_line; int error_char; + int error_code; } invalid_scripts[] = { @@ -2703,7 +2714,7 @@ static void test_parse_errors(void) /* If...End If */ L"If 0 > 1 Then\n" " x = 0 End If\n", - 1, 10 + 1, 10, -1042 }, { /* ElseIf...End If */ @@ -2711,50 +2722,50 @@ static void test_parse_errors(void) " x = 0\n" "ElseIf True Then\n" " x = 1 End If\n", - 3, 10 + 3, 10, -1042 }, { /* Else End If (no separator) */ L"If False Then\n" " x = 0\n" "Else End If\n", - 2, 5 + 2, 5, -1024 }, { /* While...End While */ L"While False\n" " x = 0 End While\n", - 1, 10 + 1, 10, -1024 }, { /* While...Wend */ L"While False\n" " x = 0 Wend\n", - 1, 10 + 1, 10, -1025 }, { /* Do While...Loop */ L"Do While False\n" " x = 0 Loop\n", - 1, 10 + 1, 10, -1025 }, { /* Do Until...Loop */ L"Do Until True\n" " x = 0 Loop\n", - 1, 10 + 1, 10, -1025 }, { /* Do...Loop While */ L"Do\n" " x = 0 Loop While False\n", - 1, 10 + 1, 10, -1025 }, { /* Do...Loop Until */ L"Do\n" " x = 0 Loop Until True\n", - 1, 10 + 1, 10, -1025 }, { /* Select...End Select */ @@ -2765,22 +2776,22 @@ static void test_parse_errors(void) " Case 42\n" " x = True End Select\n" "Call ok(x, \"wrong case\")\n", - 5, 17 + 5, 17, -1042 }, { /* Class...End Class (empty) */ L"Class C End Class", - 0, 8 + 0, 8, -1024 }, { /* Class...End Class (empty) */ L"Class C _\nEnd Class", - 1, 0 + 1, 0, -1024 }, { /* invalid use of parentheses for call statement */ L"strcomp(\"x\", \"y\")", - 0, 17 + 0, 17, -1044 }, { L"\n\n\n cint _\n throwInt(&h80001234&)", @@ -2829,6 +2840,133 @@ static void test_parse_errors(void) " throwInt &h87001234&\n" "end if\n", 2, 1 + }, + { + /* Unterminated string constant - error 1033 */ + L"x = \"hello\n", + 0, -10, -1033 + }, + { + /* Expected 'End' - error 1014 */ + L"If True Then\nx = 1\n", + 2, 0, -1014 + }, + { + /* Name redefined - error 1041 */ + L"Dim a\nDim a\n", + 1, -4, -1041 + }, + { + /* Expected identifier - error 1010 */ + L"Dim If\n", + 0, 4, -1010 + }, + { + /* Invalid character - error 1032 */ + L"x = @invalid\n", + 0, 4, -1032 + }, + { + /* Expected literal constant - error 1045 */ + L"Const x = 1 + \"a\"\n", + 0, -17, -1045 + }, + { + /* Expected '(' - error 1005 */ + L"Sub x)\nEnd Sub\n", + 0, 5, -1005 + }, + { + /* Expected '=' - error 1011 */ + L"Const x\n", + 0, 7, -1011 + }, + { + /* Expected 'To' - error 1013 */ + L"For i = 1 x 10\nNext\n", + 0, 10, -1013 + }, + { + /* Expected 'Then' - error 1017 */ + L"If True\nEnd If\n", + 0, 7, -1017 + }, + { + /* Expected 'Wend' - error 1018 */ + L"While True\nx = 1\n", + 2, 0, -1018 + }, + { + /* Expected 'Loop' - error 1019 */ + L"Do\nx = 1\n", + 2, 0, -1019 + }, + { + /* Expected 'Next' - error 1020 */ + L"For i = 1 To 10\nx = 1\n", + 2, 0, -1020 + }, + { + /* Expected 'Case' - error 1021 */ + L"Select x\nEnd Select\n", + 0, 7, -1021 + }, + { + /* Expected integer constant - error 1026 */ + L"Dim x(\"a\")\n", + 0, 6, -1026 + }, + { + /* Invalid number - error 1031 */ + L"x = 1e999\n", + 0, -9, -1031 + }, + { + /* 'loop' without 'do' - error 1038 */ + L"Loop\n", + 0, 0, -1038 + }, + { + /* Invalid 'exit' statement - error 1039 */ + L"Exit Do\n", + 0, -5, -1039 + }, + { + /* Expected 'In' - error 1046 */ + L"For Each x = arr\nNext\n", + 0, 11, -1046 + }, + { + /* Must be defined inside a Class - error 1048 */ + L"Property Get x\nEnd Property\n", + 0, 9, -1048 + }, + { + /* Expected Let or Set or Get - error 1049 */ + L"Class C\nProperty x\nEnd Property\nEnd Class\n", + 1, 9, -1049 + }, + /* TODO: Wine allows arguments on Class_Initialize/Class_Terminate + { + Class initialize/terminate no arguments - error 1053 + L"Class C\nSub Class_Initialize(x)\nEnd Sub\nEnd Class\n", + 1, 20, 1053 + }, + */ + { + /* Property Let/Set needs at least one argument - error 1054 */ + L"Class C\nProperty Let x\nEnd Property\nEnd Class\n", + 1, 14, -1054 + }, + { + /* Unexpected 'Next' - error 1055 */ + L"Next\n", + 0, 0, -1055 + }, + { + /* 'Default' must also specify 'Public' - error 1057 */ + L"Class C\nDefault Private Function f()\nf = 1\nEnd Function\nEnd Class\n", + 1, 0, -1057 } }; HRESULT hres; @@ -2838,6 +2976,7 @@ static void test_parse_errors(void) { error_line = ~0; error_char = -1; + error_code = 0; onerror_hres = S_OK; SET_EXPECT(OnScriptError); @@ -2850,6 +2989,10 @@ static void test_parse_errors(void) todo_wine_if(invalid_scripts[i].error_char < 0) ok(error_char == abs(invalid_scripts[i].error_char), "[%u] error char %ld expected %d\n", i, error_char, invalid_scripts[i].error_char); + if(invalid_scripts[i].error_code) + todo_wine_if(invalid_scripts[i].error_code < 0) + ok(error_code == abs(invalid_scripts[i].error_code), "[%u] error code %u expected %u\n", + i, error_code, abs(invalid_scripts[i].error_code)); } } diff --git a/dlls/vbscript/vbscript.rc b/dlls/vbscript/vbscript.rc index b9a1c3ad5c3..a0f7381bcf0 100644 --- a/dlls/vbscript/vbscript.rc +++ b/dlls/vbscript/vbscript.rc @@ -59,6 +59,38 @@ STRINGTABLE VBSE_INVALID_TYPELIB_VARIABLE "Variable uses an Automation type not supported in VBScript" VBSE_SERVER_NOT_FOUND "The remote server machine does not exist or is unavailable" VBSE_UNQUALIFIED_REFERENCE "Invalid or unqualified reference" + VBSE_SYNTAX_ERROR "Syntax error" + VBSE_EXPECTED_LPAREN "Expected '('" + VBSE_EXPECTED_IDENTIFIER "Expected identifier" + VBSE_EXPECTED_ASSIGN "Expected '='" + VBSE_EXPECTED_TO "Expected 'To'" + VBSE_EXPECTED_END "Expected 'End'" + VBSE_EXPECTED_THEN "Expected 'Then'" + VBSE_EXPECTED_WEND "Expected 'Wend'" + VBSE_EXPECTED_LOOP "Expected 'Loop'" + VBSE_EXPECTED_NEXT "Expected 'Next'" + VBSE_EXPECTED_CASE "Expected 'Case'" + VBSE_EXPECTED_STATEMENT "Expected statement" + VBSE_EXPECTED_END_OF_STATEMENT "Expected end of statement" + VBSE_EXPECTED_INTEGER_CONSTANT "Expected integer constant" + VBSE_EXPECTED_WHILE_UNTIL_EOS "Expected 'While', 'Until' or end of statement" + VBSE_INVALID_NUMBER "Invalid number" + VBSE_INVALID_CHAR "Invalid character" + VBSE_UNTERMINATED_STRING "Unterminated string constant" + VBSE_LOOP_WITHOUT_DO "'loop' without 'do'" + VBSE_INVALID_EXIT "Invalid 'exit' statement" + VBSE_NAME_REDEFINED "Name redefined" + VBSE_MUST_BE_FIRST_STATEMENT "Must be first statement on the line" + VBSE_CANNOT_USE_PARENS_CALLING_SUB "Cannot use parentheses when calling a Sub" + VBSE_EXPECTED_LITERAL_CONSTANT "Expected literal constant" + VBSE_EXPECTED_IN "Expected 'In'" + VBSE_EXPECTED_CLASS "Expected 'Class'" + VBSE_MUST_BE_INSIDE_CLASS "Must be defined inside a Class" + VBSE_EXPECTED_LET_SET_GET "Expected Let or Set or Get in property declaration" + VBSE_CLASS_INIT_NO_ARGS "Class initialize or terminate do not have arguments" + VBSE_PROPERTY_LET_SET_NEEDS_ARG "Property set or let must have at least one argument" + VBSE_UNEXPECTED_NEXT "Unexpected 'Next'" + VBSE_DEFAULT_MUST_BE_PUBLIC "'Default' specification must also specify 'Public'" VBS_COMPILE_ERROR "Microsoft VBScript compilation error" VBS_RUNTIME_ERROR "Microsoft VBScript runtime error" diff --git a/dlls/vbscript/vbscript_defs.h b/dlls/vbscript/vbscript_defs.h index e0e9d1cac94..06d3b8990fb 100644 --- a/dlls/vbscript/vbscript_defs.h +++ b/dlls/vbscript/vbscript_defs.h @@ -270,6 +270,38 @@ #define VBSE_INVALID_TYPELIB_VARIABLE 458 #define VBSE_SERVER_NOT_FOUND 462 #define VBSE_UNQUALIFIED_REFERENCE 505 +#define VBSE_SYNTAX_ERROR 1002 +#define VBSE_EXPECTED_LPAREN 1005 +#define VBSE_EXPECTED_IDENTIFIER 1010 +#define VBSE_EXPECTED_ASSIGN 1011 +#define VBSE_EXPECTED_TO 1013 +#define VBSE_EXPECTED_END 1014 +#define VBSE_EXPECTED_THEN 1017 +#define VBSE_EXPECTED_WEND 1018 +#define VBSE_EXPECTED_LOOP 1019 +#define VBSE_EXPECTED_NEXT 1020 +#define VBSE_EXPECTED_CASE 1021 +#define VBSE_EXPECTED_STATEMENT 1024 +#define VBSE_EXPECTED_END_OF_STATEMENT 1025 +#define VBSE_EXPECTED_INTEGER_CONSTANT 1026 +#define VBSE_EXPECTED_WHILE_UNTIL_EOS 1028 +#define VBSE_INVALID_NUMBER 1031 +#define VBSE_INVALID_CHAR 1032 +#define VBSE_UNTERMINATED_STRING 1033 +#define VBSE_LOOP_WITHOUT_DO 1038 +#define VBSE_INVALID_EXIT 1039 +#define VBSE_NAME_REDEFINED 1041 +#define VBSE_MUST_BE_FIRST_STATEMENT 1042 +#define VBSE_CANNOT_USE_PARENS_CALLING_SUB 1044 +#define VBSE_EXPECTED_LITERAL_CONSTANT 1045 +#define VBSE_EXPECTED_IN 1046 +#define VBSE_EXPECTED_CLASS 1047 +#define VBSE_MUST_BE_INSIDE_CLASS 1048 +#define VBSE_EXPECTED_LET_SET_GET 1049 +#define VBSE_CLASS_INIT_NO_ARGS 1053 +#define VBSE_PROPERTY_LET_SET_NEEDS_ARG 1054 +#define VBSE_UNEXPECTED_NEXT 1055 +#define VBSE_DEFAULT_MUST_BE_PUBLIC 1057 #define VBS_COMPILE_ERROR 4096 #define VBS_RUNTIME_ERROR 4097 -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10405
From: Francis De Brabandere <francisdb@gmail.com> Return MAKE_VBSERROR with proper error codes instead of E_FAIL for name redefinition and invalid exit statements in the compiler. --- dlls/vbscript/compile.c | 39 +++++++++++++-------------------------- dlls/vbscript/tests/run.c | 4 ++-- 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c index a3fedc950a1..0a22d116642 100644 --- a/dlls/vbscript/compile.c +++ b/dlls/vbscript/compile.c @@ -1138,8 +1138,7 @@ static HRESULT compile_dim_statement(compile_ctx_t *ctx, dim_statement_t *stat) while(1) { if(lookup_dim_decls(ctx, dim_decl->name) || lookup_args_name(ctx, dim_decl->name) || lookup_const_decls(ctx, dim_decl->name, FALSE)) { - FIXME("dim %s name redefined\n", debugstr_w(dim_decl->name)); - return E_FAIL; + return MAKE_VBSERROR(VBSE_NAME_REDEFINED); } ctx->func->var_cnt++; @@ -1201,8 +1200,7 @@ static HRESULT compile_const_statement(compile_ctx_t *ctx, const_statement_t *st if(!lookup_const_decls(ctx, decl->name, FALSE)) { if(lookup_args_name(ctx, decl->name) || lookup_dim_decls(ctx, decl->name)) { - FIXME("%s redefined\n", debugstr_w(decl->name)); - return E_FAIL; + return MAKE_VBSERROR(VBSE_NAME_REDEFINED); } } @@ -1254,8 +1252,7 @@ static HRESULT compile_exitdo_statement(compile_ctx_t *ctx) break; } if(!iter) { - FIXME("Exit Do outside Do Loop\n"); - return E_FAIL; + return MAKE_VBSERROR(VBSE_INVALID_EXIT); } if(pop_cnt) { @@ -1280,8 +1277,7 @@ static HRESULT compile_exitfor_statement(compile_ctx_t *ctx) break; } if(!iter) { - FIXME("Exit For outside For loop\n"); - return E_FAIL; + return MAKE_VBSERROR(VBSE_INVALID_EXIT); } if(pop_cnt) { @@ -1313,8 +1309,7 @@ static HRESULT exit_label(compile_ctx_t *ctx, unsigned jmp_label) static HRESULT compile_exitsub_statement(compile_ctx_t *ctx) { if(!ctx->sub_end_label) { - FIXME("Exit Sub outside Sub?\n"); - return E_FAIL; + return MAKE_VBSERROR(VBSE_INVALID_EXIT); } return exit_label(ctx, ctx->sub_end_label); @@ -1323,8 +1318,7 @@ static HRESULT compile_exitsub_statement(compile_ctx_t *ctx) static HRESULT compile_exitfunc_statement(compile_ctx_t *ctx) { if(!ctx->func_end_label) { - FIXME("Exit Function outside Function?\n"); - return E_FAIL; + return MAKE_VBSERROR(VBSE_INVALID_EXIT); } return exit_label(ctx, ctx->func_end_label); @@ -1333,8 +1327,7 @@ static HRESULT compile_exitfunc_statement(compile_ctx_t *ctx) static HRESULT compile_exitprop_statement(compile_ctx_t *ctx) { if(!ctx->prop_end_label) { - FIXME("Exit Property outside Property?\n"); - return E_FAIL; + return MAKE_VBSERROR(VBSE_INVALID_EXIT); } return exit_label(ctx, ctx->prop_end_label); @@ -1377,8 +1370,7 @@ static HRESULT collect_const_decls(compile_ctx_t *ctx, statement_t *stat) break; /* already collected */ if(lookup_args_name(ctx, decl->name) || lookup_dim_decls(ctx, decl->name)) { - FIXME("%s redefined\n", debugstr_w(decl->name)); - return E_FAIL; + return MAKE_VBSERROR(VBSE_NAME_REDEFINED); } new_decl = compiler_alloc(ctx->code, sizeof(*new_decl)); @@ -1712,8 +1704,7 @@ static HRESULT create_function(compile_ctx_t *ctx, function_decl_t *decl, functi HRESULT hres; if(lookup_dim_decls(ctx, decl->name) || lookup_const_decls(ctx, decl->name, FALSE)) { - FIXME("%s: redefinition\n", debugstr_w(decl->name)); - return E_FAIL; + return MAKE_VBSERROR(VBSE_NAME_REDEFINED); } func = compiler_alloc(ctx->code, sizeof(*func)); @@ -1835,8 +1826,7 @@ static HRESULT compile_class(compile_ctx_t *ctx, class_decl_t *class_decl) if(lookup_dim_decls(ctx, class_decl->name) || lookup_funcs_name(ctx, class_decl->name) || lookup_const_decls(ctx, class_decl->name, FALSE) || lookup_class_name(ctx, class_decl->name)) { - FIXME("%s: redefinition\n", debugstr_w(class_decl->name)); - return E_FAIL; + return MAKE_VBSERROR(VBSE_NAME_REDEFINED); } class_desc = compiler_alloc_zero(ctx->code, sizeof(*class_desc)); @@ -1908,8 +1898,7 @@ static HRESULT compile_class(compile_ctx_t *ctx, class_decl_t *class_decl) for(prop_decl = class_decl->props, i=0; prop_decl; prop_decl = prop_decl->next, i++) { if(lookup_class_funcs(class_desc, prop_decl->name)) { - FIXME("Property %s redefined\n", debugstr_w(prop_decl->name)); - return E_FAIL; + return MAKE_VBSERROR(VBSE_NAME_REDEFINED); } class_desc->props[i].name = compiler_alloc_string(ctx->code, prop_decl->name); @@ -2006,15 +1995,13 @@ static HRESULT check_script_collisions(compile_ctx_t *ctx, script_ctx_t *script) for(i = 0; i < var_cnt; i++) { if(lookup_script_identifier(ctx, script, vars[i].name)) { - FIXME("%s: redefined\n", debugstr_w(vars[i].name)); - return E_FAIL; + return MAKE_VBSERROR(VBSE_NAME_REDEFINED); } } for(class = ctx->code->classes; class; class = class->next) { if(lookup_script_identifier(ctx, script, class->name)) { - FIXME("%s: redefined\n", debugstr_w(class->name)); - return E_FAIL; + return MAKE_VBSERROR(VBSE_NAME_REDEFINED); } } diff --git a/dlls/vbscript/tests/run.c b/dlls/vbscript/tests/run.c index a641214b038..7f7d70c8ea0 100644 --- a/dlls/vbscript/tests/run.c +++ b/dlls/vbscript/tests/run.c @@ -2854,7 +2854,7 @@ static void test_parse_errors(void) { /* Name redefined - error 1041 */ L"Dim a\nDim a\n", - 1, -4, -1041 + 1, -4, 1041 }, { /* Expected identifier - error 1010 */ @@ -2929,7 +2929,7 @@ static void test_parse_errors(void) { /* Invalid 'exit' statement - error 1039 */ L"Exit Do\n", - 0, -5, -1039 + 0, -5, 1039 }, { /* Expected 'In' - error 1046 */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10405
From: Francis De Brabandere <francisdb@gmail.com> Add lex_error() helper that sets both the error code and position. Return MAKE_VBSERROR with proper error codes instead of FIXME/return 0 for unterminated strings, invalid numbers, and invalid characters. --- dlls/vbscript/lex.c | 24 +++++++++++++----------- dlls/vbscript/tests/run.c | 6 +++--- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/dlls/vbscript/lex.c b/dlls/vbscript/lex.c index 75838ac586a..7e571eaa31a 100644 --- a/dlls/vbscript/lex.c +++ b/dlls/vbscript/lex.c @@ -28,6 +28,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(vbscript); +static int lex_error(parser_ctx_t *ctx, HRESULT hres) +{ + ctx->hres = hres; + ctx->error_loc = ctx->ptr - ctx->code; + return 0; +} + static const struct { const WCHAR *word; int token; @@ -169,8 +176,7 @@ static int parse_string_literal(parser_ctx_t *ctx, const WCHAR **ret) while(ctx->ptr < ctx->end) { if(*ctx->ptr == '\n' || *ctx->ptr == '\r') { - FIXME("newline inside string literal\n"); - return 0; + return lex_error(ctx, MAKE_VBSERROR(VBSE_UNTERMINATED_STRING)); } if(*ctx->ptr == '"') { @@ -183,8 +189,7 @@ static int parse_string_literal(parser_ctx_t *ctx, const WCHAR **ret) } if(ctx->ptr == ctx->end) { - FIXME("unterminated string literal\n"); - return 0; + return lex_error(ctx, MAKE_VBSERROR(VBSE_UNTERMINATED_STRING)); } len += ctx->ptr-ptr; @@ -298,8 +303,7 @@ static int parse_numeric_literal(parser_ctx_t *ctx, void **ret) } if(!is_digit(*ctx->ptr)) { - FIXME("Invalid numeric literal\n"); - return 0; + return lex_error(ctx, MAKE_VBSERROR(VBSE_INVALID_NUMBER)); } use_int = FALSE; @@ -315,8 +319,7 @@ static int parse_numeric_literal(parser_ctx_t *ctx, void **ret) } if(sign*e + exp > INT_MAX/100) { - FIXME("Invalid numeric literal\n"); - return 0; + return lex_error(ctx, MAKE_VBSERROR(VBSE_INVALID_NUMBER)); } } while(is_digit(*ctx->ptr)); @@ -330,8 +333,7 @@ static int parse_numeric_literal(parser_ctx_t *ctx, void **ret) r = exp>=0 ? d*pow(10, exp) : d/pow(10, -exp); if(isinf(r)) { - FIXME("Invalid numeric literal\n"); - return 0; + return lex_error(ctx, MAKE_VBSERROR(VBSE_INVALID_NUMBER)); } *(double*)ret = r; @@ -510,7 +512,7 @@ static int parse_next_token(void *lval, unsigned *loc, parser_ctx_t *ctx) } return '>'; default: - FIXME("Unhandled char %c in %s\n", *ctx->ptr, debugstr_w(ctx->ptr)); + return lex_error(ctx, MAKE_VBSERROR(VBSE_INVALID_CHAR)); } return 0; diff --git a/dlls/vbscript/tests/run.c b/dlls/vbscript/tests/run.c index 7f7d70c8ea0..97eede9c906 100644 --- a/dlls/vbscript/tests/run.c +++ b/dlls/vbscript/tests/run.c @@ -2844,7 +2844,7 @@ static void test_parse_errors(void) { /* Unterminated string constant - error 1033 */ L"x = \"hello\n", - 0, -10, -1033 + 0, 10, 1033 }, { /* Expected 'End' - error 1014 */ @@ -2864,7 +2864,7 @@ static void test_parse_errors(void) { /* Invalid character - error 1032 */ L"x = @invalid\n", - 0, 4, -1032 + 0, 4, 1032 }, { /* Expected literal constant - error 1045 */ @@ -2919,7 +2919,7 @@ static void test_parse_errors(void) { /* Invalid number - error 1031 */ L"x = 1e999\n", - 0, -9, -1031 + 0, 9, 1031 }, { /* 'loop' without 'do' - error 1038 */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10405
From: Francis De Brabandere <francisdb@gmail.com> Return MAKE_VBSERROR with proper error codes instead of E_FAIL for invalid parenthesized call statements, class function redefinition, and Default Private declarations. --- dlls/vbscript/parser.y | 10 ++++------ dlls/vbscript/tests/run.c | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/dlls/vbscript/parser.y b/dlls/vbscript/parser.y index 4977358b333..ecd6cc7502f 100644 --- a/dlls/vbscript/parser.y +++ b/dlls/vbscript/parser.y @@ -514,6 +514,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; } | tPUBLIC { $$ = 0; } | tPRIVATE { $$ = STORAGE_IS_PRIVATE; } @@ -760,8 +761,7 @@ static call_expression_t *make_call_expression(parser_ctx_t *ctx, expression_t * } if(call_expr->args->next) { - FIXME("Invalid syntax: invalid use of parentheses for arguments\n"); - ctx->hres = E_FAIL; + ctx->hres = MAKE_VBSERROR(VBSE_CANNOT_USE_PARENS_CALLING_SUB); ctx->error_loc = ctx->ptr - ctx->code; return NULL; } @@ -1136,15 +1136,13 @@ static class_decl_t *add_class_function(parser_ctx_t *ctx, class_decl_t *class_d for(iter = class_decl->funcs; iter; iter = iter->next) { if(!wcsicmp(iter->name, decl->name)) { if(decl->type == FUNC_SUB || decl->type == FUNC_FUNCTION) { - FIXME("Redefinition of %s::%s\n", debugstr_w(class_decl->name), debugstr_w(decl->name)); - ctx->hres = E_FAIL; + ctx->hres = MAKE_VBSERROR(VBSE_NAME_REDEFINED); return NULL; } while(1) { if(iter->type == decl->type) { - FIXME("Redefinition of %s::%s\n", debugstr_w(class_decl->name), debugstr_w(decl->name)); - ctx->hres = E_FAIL; + ctx->hres = MAKE_VBSERROR(VBSE_NAME_REDEFINED); return NULL; } if(!iter->next_prop_func) diff --git a/dlls/vbscript/tests/run.c b/dlls/vbscript/tests/run.c index 97eede9c906..027f94a3560 100644 --- a/dlls/vbscript/tests/run.c +++ b/dlls/vbscript/tests/run.c @@ -2791,7 +2791,7 @@ static void test_parse_errors(void) { /* invalid use of parentheses for call statement */ L"strcomp(\"x\", \"y\")", - 0, 17, -1044 + 0, 17, 1044 }, { L"\n\n\n cint _\n throwInt(&h80001234&)", @@ -2966,7 +2966,7 @@ static void test_parse_errors(void) { /* 'Default' must also specify 'Public' - error 1057 */ L"Class C\nDefault Private Function f()\nf = 1\nEnd Function\nEnd Class\n", - 1, 0, -1057 + 1, 0, 1057 } }; HRESULT hres; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10405
From: Francis De Brabandere <francisdb@gmail.com> When Option Explicit is set and an undefined variable is used, return VBS error 500 (Variable is undefined) with the variable name in the description, matching Windows behavior. Previously Wine returned E_FAIL with no description. --- dlls/vbscript/interp.c | 58 +++++++++++++++++++++++++++-------- dlls/vbscript/tests/run.c | 33 ++++++++++++++++++++ dlls/vbscript/vbscript.rc | 1 + dlls/vbscript/vbscript_defs.h | 1 + 4 files changed, 80 insertions(+), 13 deletions(-) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 79225af27b9..90387a58606 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -301,6 +301,37 @@ static void clear_error_loc(script_ctx_t *ctx) } } +static HRESULT throw_error(script_ctx_t *ctx, HRESULT error, const WCHAR *identifier) +{ + BSTR desc, source; + + desc = get_vbscript_string(HRESULT_CODE(error)); + if(desc && identifier) { + unsigned desc_len = SysStringLen(desc); + unsigned ident_len = lstrlenW(identifier); + /* format: "Error text: 'identifier'" */ + BSTR new_desc = SysAllocStringLen(NULL, desc_len + 3 + ident_len + 1); + if(new_desc) { + memcpy(new_desc, desc, desc_len * sizeof(WCHAR)); + new_desc[desc_len] = ':'; + new_desc[desc_len + 1] = ' '; + new_desc[desc_len + 2] = '\''; + memcpy(new_desc + desc_len + 3, identifier, ident_len * sizeof(WCHAR)); + new_desc[desc_len + 3 + ident_len] = '\''; + SysFreeString(desc); + desc = new_desc; + } + } + + source = get_vbscript_string(VBS_RUNTIME_ERROR); + + clear_ei(&ctx->ei); + ctx->ei.scode = error; + ctx->ei.bstrDescription = desc; + ctx->ei.bstrSource = source; + return SCRIPT_E_RECORDED; +} + static inline VARIANT *stack_pop(exec_ctx_t *ctx) { assert(ctx->top); @@ -671,6 +702,8 @@ static HRESULT do_icall(exec_ctx_t *ctx, VARIANT *res, BSTR identifier, unsigned V_BYREF(res) = new; break; } + if(ctx->func->code_ctx->option_explicit) + return throw_error(ctx->script, MAKE_VBSERROR(VBSE_VARIABLE_NOT_DEFINED), identifier); FIXME("%s not found\n", debugstr_w(identifier)); return DISP_E_UNKNOWNNAME; } @@ -903,21 +936,20 @@ static HRESULT assign_ident(exec_ctx_t *ctx, BSTR name, WORD flags, DISPPARAMS * case REF_CONST: FIXME("REF_CONST\n"); return E_NOTIMPL; - case REF_NONE: - if(ctx->func->code_ctx->option_explicit) { - FIXME("throw exception\n"); - hres = E_FAIL; - }else { - VARIANT *new_var; + case REF_NONE: { + VARIANT *new_var; - if(arg_cnt(dp)) - return DISP_E_TYPEMISMATCH; + if(ctx->func->code_ctx->option_explicit) + return throw_error(ctx->script, MAKE_VBSERROR(VBSE_VARIABLE_NOT_DEFINED), name); - TRACE("creating variable %s\n", debugstr_w(name)); - hres = add_dynamic_var(ctx, name, FALSE, &new_var); - if(SUCCEEDED(hres)) - hres = assign_value(ctx, new_var, dp->rgvarg, flags); - } + if(arg_cnt(dp)) + return DISP_E_TYPEMISMATCH; + + TRACE("creating variable %s\n", debugstr_w(name)); + hres = add_dynamic_var(ctx, name, FALSE, &new_var); + if(SUCCEEDED(hres)) + hres = assign_value(ctx, new_var, dp->rgvarg, flags); + } } return hres; diff --git a/dlls/vbscript/tests/run.c b/dlls/vbscript/tests/run.c index 027f94a3560..3af76deb955 100644 --- a/dlls/vbscript/tests/run.c +++ b/dlls/vbscript/tests/run.c @@ -2199,6 +2199,38 @@ static HRESULT parse_script_wr(const WCHAR *src) return hres; } +static void test_option_explicit_errors(void) +{ + IActiveScriptError *error; + EXCEPINFO ei; + HRESULT hres; + + /* Without Option Explicit: assigning to undefined variable should succeed (implicit creation) */ + parse_script_wf(SCRIPTITEM_GLOBALMEMBERS, L"x = 1\nCall ok(x = 1, \"x = \" & x)"); + + /* Without Option Explicit: reading undefined variable should succeed and return Empty */ + parse_script_wf(SCRIPTITEM_GLOBALMEMBERS, L"Call ok(getVT(y) = \"VT_EMPTY*\", \"getVT(y) = \" & getVT(y))"); + + /* Option Explicit: assigning to undefined variable should give error 500 */ + store_script_error = &error; + SET_EXPECT(OnScriptError); + hres = parse_script_wr(L"Option Explicit\nx = 1"); + ok(hres == MAKE_VBSERROR(500), "expected MAKE_VBSERROR(500), got: %08lx\n", hres); + CHECK_CALLED(OnScriptError); + + memset(&ei, 0, sizeof(ei)); + hres = IActiveScriptError_GetExceptionInfo(error, &ei); + ok(hres == S_OK, "GetExceptionInfo returned %08lx\n", hres); + ok(ei.scode == MAKE_VBSERROR(500), "scode = %lx\n", ei.scode); + if(is_english) + ok(ei.bstrDescription && !wcscmp(ei.bstrDescription, L"Variable is undefined: 'x'"), + "bstrDescription = %s\n", wine_dbgstr_w(ei.bstrDescription)); + SysFreeString(ei.bstrSource); + SysFreeString(ei.bstrDescription); + SysFreeString(ei.bstrHelpFile); + IActiveScriptError_Release(error); +} + static void test_parse_context(void) { IActiveScriptParse *parser; @@ -3657,6 +3689,7 @@ static void run_tests(void) test_gc(); test_msgbox(); test_isexpression(); + test_option_explicit_errors(); test_parse_errors(); test_parse_context(); test_callbacks(); diff --git a/dlls/vbscript/vbscript.rc b/dlls/vbscript/vbscript.rc index a0f7381bcf0..2a18ee2f0a8 100644 --- a/dlls/vbscript/vbscript.rc +++ b/dlls/vbscript/vbscript.rc @@ -58,6 +58,7 @@ STRINGTABLE VBSE_INVALID_DLL_FUNCTION_NAME "Specified DLL function not found" VBSE_INVALID_TYPELIB_VARIABLE "Variable uses an Automation type not supported in VBScript" VBSE_SERVER_NOT_FOUND "The remote server machine does not exist or is unavailable" + VBSE_VARIABLE_NOT_DEFINED "Variable is undefined" VBSE_UNQUALIFIED_REFERENCE "Invalid or unqualified reference" VBSE_SYNTAX_ERROR "Syntax error" VBSE_EXPECTED_LPAREN "Expected '('" diff --git a/dlls/vbscript/vbscript_defs.h b/dlls/vbscript/vbscript_defs.h index 06d3b8990fb..2a351c4ebd2 100644 --- a/dlls/vbscript/vbscript_defs.h +++ b/dlls/vbscript/vbscript_defs.h @@ -269,6 +269,7 @@ #define VBSE_INVALID_DLL_FUNCTION_NAME 453 #define VBSE_INVALID_TYPELIB_VARIABLE 458 #define VBSE_SERVER_NOT_FOUND 462 +#define VBSE_VARIABLE_NOT_DEFINED 500 #define VBSE_UNQUALIFIED_REFERENCE 505 #define VBSE_SYNTAX_ERROR 1002 #define VBSE_EXPECTED_LPAREN 1005 -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10405
participants (2)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb)