From f568b48e66dea7198afab67b81aa45bbeb575fc6 Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Thu, 1 Apr 2021 18:18:31 +0200 Subject: [PATCH] jscript: Get dispid and call invoke in a single step when setting a member property. Signed-off-by: Jacek Caban Signed-off-by: Alexandre Julliard --- dlls/jscript/compile.c | 91 ++++++++++++++++++-------------------- dlls/jscript/dispex.c | 32 ++++++++++++++ dlls/jscript/engine.c | 54 ++++++++++++++++++++++ dlls/jscript/engine.h | 2 + dlls/jscript/jscript.h | 1 + dlls/jscript/tests/lang.js | 6 +++ dlls/jscript/tests/run.c | 19 ++++++++ 7 files changed, 157 insertions(+), 48 deletions(-) diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index dea8db1f37e..08a5e3af64b 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -466,18 +466,11 @@ static HRESULT emit_identifier(compiler_ctx_t *ctx, const WCHAR *identifier) return push_instr_bstr(ctx, OP_ident, identifier); } -static HRESULT compile_memberid_expression(compiler_ctx_t *ctx, expression_t *expr, unsigned flags) +static HRESULT emit_member_expression(compiler_ctx_t *ctx, expression_t *expr) { - HRESULT hres = S_OK; + HRESULT hres; - switch(expr->type) { - case EXPR_IDENT: { - identifier_expression_t *ident_expr = (identifier_expression_t*)expr; - - hres = emit_identifier_ref(ctx, ident_expr->identifier, flags); - break; - } - case EXPR_ARRAY: { + if(expr->type == EXPR_ARRAY) { binary_expression_t *array_expr = (binary_expression_t*)expr; hres = compile_expression(ctx, array_expr->expression1, TRUE); @@ -488,18 +481,18 @@ static HRESULT compile_memberid_expression(compiler_ctx_t *ctx, expression_t *ex if(FAILED(hres)) return hres; - hres = push_instr_uint(ctx, OP_memberid, flags); - break; - } - case EXPR_MEMBER: { + if(!push_instr(ctx, OP_to_string)) + return E_OUTOFMEMORY; + }else { member_expression_t *member_expr = (member_expression_t*)expr; jsstr_t *jsstr; + assert(expr->type == EXPR_MEMBER); + hres = compile_expression(ctx, member_expr->expression, TRUE); if(FAILED(hres)) return hres; - /* FIXME: Potential optimization */ jsstr = compiler_alloc_string(ctx, member_expr->identifier); if(!jsstr) return E_OUTOFMEMORY; @@ -507,14 +500,25 @@ static HRESULT compile_memberid_expression(compiler_ctx_t *ctx, expression_t *ex hres = push_instr_str(ctx, OP_str, jsstr); if(FAILED(hres)) return hres; - - hres = push_instr_uint(ctx, OP_memberid, flags); - break; - } - DEFAULT_UNREACHABLE; } - return hres; + return S_OK; +} + +static HRESULT compile_memberid_expression(compiler_ctx_t *ctx, expression_t *expr, unsigned flags) +{ + HRESULT hres; + + if(expr->type == EXPR_IDENT) { + identifier_expression_t *ident_expr = (identifier_expression_t*)expr; + return emit_identifier_ref(ctx, ident_expr->identifier, flags); + } + + hres = emit_member_expression(ctx, expr); + if(FAILED(hres)) + return hres; + + return push_instr_uint(ctx, OP_memberid, flags); } static HRESULT compile_increment_expression(compiler_ctx_t *ctx, unary_expression_t *expr, jsop_t op, int n) @@ -734,7 +738,7 @@ static HRESULT compile_delete_expression(compiler_ctx_t *ctx, unary_expression_t static HRESULT compile_assign_expression(compiler_ctx_t *ctx, binary_expression_t *expr, jsop_t op) { - BOOL use_throw_path = FALSE; + jsop_t assign_op = OP_throw_ref; unsigned arg_cnt = 0; HRESULT hres; @@ -770,33 +774,30 @@ static HRESULT compile_assign_expression(compiler_ctx_t *ctx, binary_expression_ if(!push_instr(ctx, OP_push_acc)) return E_OUTOFMEMORY; } - }else { - use_throw_path = TRUE; + assign_op = OP_assign_call; } }else if(is_memberid_expr(expr->expression1->type)) { - hres = compile_memberid_expression(ctx, expr->expression1, fdexNameEnsure); - if(FAILED(hres)) - return hres; - if(op != OP_LAST && !push_instr(ctx, OP_refval)) - return E_OUTOFMEMORY; - }else { - use_throw_path = TRUE; + if(op != OP_LAST || expr->expression1->type == EXPR_IDENT) { + hres = compile_memberid_expression(ctx, expr->expression1, fdexNameEnsure); + if(FAILED(hres)) + return hres; + if(op != OP_LAST && !push_instr(ctx, OP_refval)) + return E_OUTOFMEMORY; + assign_op = OP_assign; + }else { + hres = emit_member_expression(ctx, expr->expression1); + if(FAILED(hres)) + return hres; + assign_op = OP_set_member; + } } - if(use_throw_path) { + if(assign_op == OP_throw_ref) { /* Illegal assignment: evaluate and throw */ hres = compile_expression(ctx, expr->expression1, TRUE); if(FAILED(hres)) return hres; - - hres = compile_expression(ctx, expr->expression2, TRUE); - if(FAILED(hres)) - return hres; - - if(op != OP_LAST && !push_instr(ctx, op)) - return E_OUTOFMEMORY; - - return push_instr_uint(ctx, OP_throw_ref, JS_E_ILLEGAL_ASSIGN); + arg_cnt = JS_E_ILLEGAL_ASSIGN; } hres = compile_expression(ctx, expr->expression2, TRUE); @@ -806,13 +807,7 @@ static HRESULT compile_assign_expression(compiler_ctx_t *ctx, binary_expression_ if(op != OP_LAST && !push_instr(ctx, op)) return E_OUTOFMEMORY; - if(arg_cnt) - return push_instr_uint(ctx, OP_assign_call, arg_cnt); - - if(!push_instr(ctx, OP_assign)) - return E_OUTOFMEMORY; - - return S_OK; + return push_instr_uint(ctx, assign_op, arg_cnt); } static HRESULT compile_typeof_expression(compiler_ctx_t *ctx, unary_expression_t *expr) diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 973ea4c7142..0f0907063f8 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -2203,6 +2203,38 @@ HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t val) return hres; } +HRESULT disp_propput_name(script_ctx_t *ctx, IDispatch *disp, const WCHAR *name, jsval_t val) +{ + jsdisp_t *jsdisp; + HRESULT hres; + + jsdisp = iface_to_jsdisp(disp); + if(!jsdisp || jsdisp->ctx != ctx) { + IDispatchEx *dispex; + BSTR str; + DISPID id; + + if(!(str = SysAllocString(name))) + return E_OUTOFMEMORY; + + hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); + if(SUCCEEDED(hres)) { + hres = IDispatchEx_GetDispID(dispex, str, make_grfdex(ctx, fdexNameEnsure|fdexNameCaseSensitive), &id); + IDispatchEx_Release(dispex); + }else { + TRACE("using IDispatch\n"); + hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &str, 1, 0, &id); + } + SysFreeString(str); + if(FAILED(hres)) + return hres; + + return disp_propput(ctx, disp, id, val); + } + + return jsdisp_propput_name(jsdisp, name, val); +} + HRESULT jsdisp_propget_name(jsdisp_t *obj, const WCHAR *name, jsval_t *val) { dispex_prop_t *prop; diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index 18dc4c3f139..018597829ed 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -2511,6 +2511,25 @@ static HRESULT interp_rshift2(script_ctx_t *ctx) return stack_push(ctx, jsval_number(l >> (r&0x1f))); } +/* ECMA-262 3rd Edition 9.8 */ +static HRESULT interp_to_string(script_ctx_t *ctx) +{ + jsstr_t *str; + jsval_t v; + HRESULT hres; + + v = stack_pop(ctx); + TRACE("%s\n", debugstr_jsval(v)); + hres = to_string(ctx, v, &str); + jsval_release(v); + if(FAILED(hres)) { + WARN("failed %08x\n", hres); + return hres; + } + + return stack_push(ctx, jsval_string(str)); +} + /* ECMA-262 3rd Edition 11.13.1 */ static HRESULT interp_assign(script_ctx_t *ctx) { @@ -2537,6 +2556,41 @@ static HRESULT interp_assign(script_ctx_t *ctx) return stack_push(ctx, v); } +/* ECMA-262 3rd Edition 11.13.1 */ +static HRESULT interp_set_member(script_ctx_t *ctx) +{ + jsval_t objv, namev, value; + const WCHAR *name; + IDispatch *obj; + HRESULT hres; + + value = stack_pop(ctx); + namev = stack_pop(ctx); + assert(is_string(namev)); + objv = stack_pop(ctx); + + TRACE("%s.%s = %s\n", debugstr_jsval(objv), debugstr_jsval(namev), debugstr_jsval(value)); + + hres = to_object(ctx, objv, &obj); + jsval_release(objv); + if(SUCCEEDED(hres) && !(name = jsstr_flatten(get_string(namev)))) { + IDispatch_Release(obj); + hres = E_OUTOFMEMORY; + } + if(SUCCEEDED(hres)) { + hres = disp_propput_name(ctx, obj, name, value); + IDispatch_Release(obj); + jsstr_release(get_string(namev)); + } + if(FAILED(hres)) { + WARN("failed %08x\n", hres); + jsval_release(value); + return hres; + } + + return stack_push(ctx, value); +} + /* JScript extension */ static HRESULT interp_assign_call(script_ctx_t *ctx) { diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index eca27cb1460..483c32232c1 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -90,8 +90,10 @@ X(typeofident,1, 0,0) \ X(refval, 1, 0,0) \ X(ret, 0, ARG_UINT, 0) \ + X(set_member, 1, 0,0) \ X(setret, 1, 0,0) \ X(sub, 1, 0,0) \ + X(to_string, 1, 0,0) \ X(undefined, 1, 0,0) \ X(void, 1, 0,0) \ X(xor, 1, 0,0) diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index d7d9dd7a4e3..c8ec7622caf 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -300,6 +300,7 @@ HRESULT jsdisp_call(jsdisp_t*,DISPID,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_H HRESULT jsdisp_call_name(jsdisp_t*,const WCHAR*,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN; HRESULT disp_propget(script_ctx_t*,IDispatch*,DISPID,jsval_t*) DECLSPEC_HIDDEN; HRESULT disp_propput(script_ctx_t*,IDispatch*,DISPID,jsval_t) DECLSPEC_HIDDEN; +HRESULT disp_propput_name(script_ctx_t*,IDispatch*,const WCHAR*,jsval_t) DECLSPEC_HIDDEN; HRESULT jsdisp_propget(jsdisp_t*,DISPID,jsval_t*) DECLSPEC_HIDDEN; HRESULT jsdisp_propput(jsdisp_t*,const WCHAR*,DWORD,jsval_t) DECLSPEC_HIDDEN; HRESULT jsdisp_propput_name(jsdisp_t*,const WCHAR*,jsval_t) DECLSPEC_HIDDEN; diff --git a/dlls/jscript/tests/lang.js b/dlls/jscript/tests/lang.js index fa8407cecea..9e6e6ad239f 100644 --- a/dlls/jscript/tests/lang.js +++ b/dlls/jscript/tests/lang.js @@ -1834,6 +1834,12 @@ ok(""+str === "test", "''+str = " + str); ok((function (){return 1;})() === 1, "(function (){return 1;})() = " + (function (){return 1;})()); +(function() { + var order = "", o = {}; + o[order += "1,", { toString: function() { order += "2,"; } }] = (order += "3"); + ok(order === "1,2,3", "array expression order = " + order); +})(); + var re = /=(\?|%3F)/g; ok(re.source === "=(\\?|%3F)", "re.source = " + re.source); diff --git a/dlls/jscript/tests/run.c b/dlls/jscript/tests/run.c index 281774cb2ba..ef0f39f1317 100644 --- a/dlls/jscript/tests/run.c +++ b/dlls/jscript/tests/run.c @@ -111,6 +111,7 @@ DEFINE_EXPECT(testobj_onlydispid_d); DEFINE_EXPECT(testobj_onlydispid_i); DEFINE_EXPECT(testobj_notexists_d); DEFINE_EXPECT(testobj_newenum); +DEFINE_EXPECT(testobj_getidfail_d); DEFINE_EXPECT(enumvariant_next_0); DEFINE_EXPECT(enumvariant_next_1); DEFINE_EXPECT(enumvariant_reset); @@ -480,6 +481,10 @@ static HRESULT WINAPI testObj_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD test_grfdex(grfdex, fdexNameCaseSensitive); return DISP_E_UNKNOWNNAME; } + if(!lstrcmpW(bstrName, L"getIDFail")) { + CHECK_EXPECT(testobj_getidfail_d); + return E_FAIL; + } ok(0, "unexpected name %s\n", wine_dbgstr_w(bstrName)); return E_NOTIMPL; @@ -3374,6 +3379,20 @@ static BOOL run_tests(void) CHECK_CALLED(testobj_onlydispid_d); CHECK_CALLED(testobj_onlydispid_i); + SET_EXPECT(testobj_getidfail_d); + hres = parse_script(SCRIPTITEM_GLOBALMEMBERS, L"testObj.notExists = testObj.getIDFail;"); + ok(hres == E_FAIL, "parse_script returned %08x\n", hres); + CHECK_CALLED(testobj_getidfail_d); + + SET_EXPECT(global_propget_d); + SET_EXPECT(global_propget_i); + SET_EXPECT(testobj_getidfail_d); + hres = parse_script(SCRIPTITEM_GLOBALMEMBERS, L"testObj.getIDFail = testPropGet;"); + ok(hres == E_FAIL, "parse_script returned %08x\n", hres); + CHECK_CALLED(global_propget_d); + CHECK_CALLED(global_propget_i); + CHECK_CALLED(testobj_getidfail_d); + SET_EXPECT(global_propargput_d); SET_EXPECT(global_propargput_i); run_script(L"var t=0; propArgPutG(t++, t++) = t++;");