jscript: Get dispid and call invoke in a single step when setting a member property.

Signed-off-by: Jacek Caban <jacek@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Jacek Caban 2021-04-01 18:18:31 +02:00 committed by Alexandre Julliard
parent b80da20656
commit f568b48e66
7 changed files with 157 additions and 48 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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)
{

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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++;");