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:
parent
b80da20656
commit
f568b48e66
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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++;");
|
||||
|
|
Loading…
Reference in New Issue