jscript: Use bytecode for calls on identifier and member expressions.
This commit is contained in:
parent
18576b36e4
commit
3aa7cee00e
|
@ -154,6 +154,24 @@ static HRESULT push_instr_bstr(compiler_ctx_t *ctx, jsop_t op, const WCHAR *arg)
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT push_instr_bstr_uint(compiler_ctx_t *ctx, jsop_t op, const WCHAR *arg1, unsigned arg2)
|
||||
{
|
||||
unsigned instr;
|
||||
WCHAR *str;
|
||||
|
||||
str = compiler_alloc_bstr(ctx, arg1);
|
||||
if(!str)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
instr = push_instr(ctx, op);
|
||||
if(instr == -1)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
instr_ptr(ctx, instr)->arg1.bstr = str;
|
||||
instr_ptr(ctx, instr)->arg2.uint = arg2;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT push_instr_double(compiler_ctx_t *ctx, jsop_t op, double arg)
|
||||
{
|
||||
unsigned instr;
|
||||
|
@ -222,6 +240,58 @@ static HRESULT compile_member_expression(compiler_ctx_t *ctx, member_expression_
|
|||
return push_instr_bstr(ctx, OP_member, expr->identifier);
|
||||
}
|
||||
|
||||
static inline BOOL is_memberid_expr(expression_type_t type)
|
||||
{
|
||||
return type == EXPR_IDENT || type == EXPR_MEMBER || type == EXPR_ARRAY;
|
||||
}
|
||||
|
||||
static HRESULT compile_memberid_expression(compiler_ctx_t *ctx, expression_t *expr, unsigned flags)
|
||||
{
|
||||
HRESULT hres = S_OK;
|
||||
|
||||
switch(expr->type) {
|
||||
case EXPR_IDENT: {
|
||||
identifier_expression_t *ident_expr = (identifier_expression_t*)expr;
|
||||
|
||||
hres = push_instr_bstr_uint(ctx, OP_identid, ident_expr->identifier, flags);
|
||||
break;
|
||||
}
|
||||
case EXPR_ARRAY: {
|
||||
array_expression_t *array_expr = (array_expression_t*)expr;
|
||||
|
||||
hres = compile_expression(ctx, array_expr->member_expr);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
hres = compile_expression(ctx, array_expr->expression);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
hres = push_instr_uint(ctx, OP_memberid, flags);
|
||||
break;
|
||||
}
|
||||
case EXPR_MEMBER: {
|
||||
member_expression_t *member_expr = (member_expression_t*)expr;
|
||||
|
||||
hres = compile_expression(ctx, member_expr->expression);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
/* FIXME: Potential optimization */
|
||||
hres = push_instr_str(ctx, OP_str, member_expr->identifier);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
hres = push_instr_uint(ctx, OP_memberid, flags);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return hres;
|
||||
}
|
||||
|
||||
/* ECMA-262 3rd Edition 11.14 */
|
||||
static HRESULT compile_comma_expression(compiler_ctx_t *ctx, binary_expression_t *expr)
|
||||
{
|
||||
|
@ -325,6 +395,40 @@ static HRESULT compile_interp_fallback(compiler_ctx_t *ctx, expression_t *expr)
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT compile_call_expression(compiler_ctx_t *ctx, call_expression_t *expr, BOOL *no_ret)
|
||||
{
|
||||
unsigned arg_cnt = 0;
|
||||
argument_t *arg;
|
||||
unsigned instr;
|
||||
HRESULT hres;
|
||||
|
||||
if(!is_memberid_expr(expr->expression->type)) {
|
||||
expr->expr.eval = call_expression_eval;
|
||||
return compile_interp_fallback(ctx, &expr->expr);
|
||||
}
|
||||
|
||||
hres = compile_memberid_expression(ctx, expr->expression, 0);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
for(arg = expr->argument_list; arg; arg = arg->next) {
|
||||
hres = compile_expression(ctx, arg->expr);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
arg_cnt++;
|
||||
}
|
||||
|
||||
instr = push_instr(ctx, OP_call_member);
|
||||
if(instr == -1)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
instr_ptr(ctx, instr)->arg1.uint = arg_cnt;
|
||||
instr_ptr(ctx, instr)->arg2.lng = no_ret == NULL;
|
||||
if(no_ret)
|
||||
*no_ret = TRUE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT compile_delete_expression(compiler_ctx_t *ctx, unary_expression_t *expr)
|
||||
{
|
||||
HRESULT hres;
|
||||
|
@ -373,47 +477,7 @@ static HRESULT compile_assign_expression(compiler_ctx_t *ctx, binary_expression_
|
|||
{
|
||||
HRESULT hres;
|
||||
|
||||
switch(expr->expression1->type) {
|
||||
case EXPR_IDENT: {
|
||||
identifier_expression_t *ident_expr = (identifier_expression_t*)expr->expression1;
|
||||
|
||||
hres = push_instr_bstr(ctx, OP_identid, ident_expr->identifier);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
break;
|
||||
}
|
||||
case EXPR_ARRAY: {
|
||||
array_expression_t *array_expr = (array_expression_t*)expr->expression1;
|
||||
|
||||
hres = compile_expression(ctx, array_expr->member_expr);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
hres = compile_expression(ctx, array_expr->expression);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
if(push_instr(ctx, OP_memberid) == -1)
|
||||
return E_OUTOFMEMORY;
|
||||
break;
|
||||
}
|
||||
case EXPR_MEMBER: {
|
||||
member_expression_t *member_expr = (member_expression_t*)expr->expression1;
|
||||
|
||||
hres = compile_expression(ctx, member_expr->expression);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
/* FIXME: Potential optimization */
|
||||
hres = push_instr_str(ctx, OP_str, member_expr->identifier);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
if(push_instr(ctx, OP_memberid) == -1)
|
||||
return E_OUTOFMEMORY;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if(!is_memberid_expr(expr->expression1->type)) {
|
||||
hres = compile_expression(ctx, expr->expression1);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
@ -428,6 +492,10 @@ static HRESULT compile_assign_expression(compiler_ctx_t *ctx, binary_expression_
|
|||
return push_instr_uint(ctx, OP_throw, JS_E_ILLEGAL_ASSIGN);
|
||||
}
|
||||
|
||||
hres = compile_memberid_expression(ctx, expr->expression1, fdexNameEnsure);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
if(op != OP_LAST && push_instr(ctx, OP_refval) == -1)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
|
@ -480,7 +548,7 @@ static HRESULT compile_literal(compiler_ctx_t *ctx, literal_t *literal)
|
|||
}
|
||||
}
|
||||
|
||||
static HRESULT compile_expression(compiler_ctx_t *ctx, expression_t *expr)
|
||||
static HRESULT compile_expression_noret(compiler_ctx_t *ctx, expression_t *expr, BOOL *no_ret)
|
||||
{
|
||||
switch(expr->type) {
|
||||
case EXPR_ADD:
|
||||
|
@ -507,6 +575,8 @@ static HRESULT compile_expression(compiler_ctx_t *ctx, expression_t *expr)
|
|||
return compile_unary_expression(ctx, (unary_expression_t*)expr, OP_bneg);
|
||||
case EXPR_BOR:
|
||||
return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_or);
|
||||
case EXPR_CALL:
|
||||
return compile_call_expression(ctx, (call_expression_t*)expr, no_ret);
|
||||
case EXPR_COMMA:
|
||||
return compile_comma_expression(ctx, (binary_expression_t*)expr);
|
||||
case EXPR_COND:
|
||||
|
@ -569,6 +639,11 @@ static HRESULT compile_expression(compiler_ctx_t *ctx, expression_t *expr)
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT compile_expression(compiler_ctx_t *ctx, expression_t *expr)
|
||||
{
|
||||
return compile_expression_noret(ctx, expr, NULL);
|
||||
}
|
||||
|
||||
void release_bytecode(bytecode_t *code)
|
||||
{
|
||||
unsigned i;
|
||||
|
@ -587,8 +662,9 @@ void release_compiler(compiler_ctx_t *ctx)
|
|||
heap_free(ctx);
|
||||
}
|
||||
|
||||
HRESULT compile_subscript(parser_ctx_t *parser, expression_t *expr, unsigned *ret_off)
|
||||
HRESULT compile_subscript(parser_ctx_t *parser, expression_t *expr, BOOL do_ret, unsigned *ret_off)
|
||||
{
|
||||
BOOL no_ret = FALSE;
|
||||
HRESULT hres;
|
||||
|
||||
if(!parser->code) {
|
||||
|
@ -608,7 +684,7 @@ HRESULT compile_subscript(parser_ctx_t *parser, expression_t *expr, unsigned *re
|
|||
}
|
||||
|
||||
*ret_off = parser->compiler->code_off;
|
||||
hres = compile_expression(parser->compiler, expr);
|
||||
hres = compile_expression_noret(parser->compiler, expr, do_ret ? NULL : &no_ret);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
|
|
|
@ -182,12 +182,12 @@ static inline IDispatch *stack_pop_objid(exec_ctx_t *ctx, DISPID *id)
|
|||
return V_DISPATCH(stack_pop(ctx));
|
||||
}
|
||||
|
||||
static inline IDispatch *stack_top_objid(exec_ctx_t *ctx, DISPID *id)
|
||||
static inline IDispatch *stack_topn_objid(exec_ctx_t *ctx, unsigned n, DISPID *id)
|
||||
{
|
||||
assert(V_VT(stack_top(ctx)) == VT_INT && V_VT(stack_topn(ctx, 1)) == VT_DISPATCH);
|
||||
assert(V_VT(stack_topn(ctx, n)) == VT_INT && V_VT(stack_topn(ctx, n+1)) == VT_DISPATCH);
|
||||
|
||||
*id = V_INT(stack_top(ctx));
|
||||
return V_DISPATCH(stack_topn(ctx, 1));
|
||||
*id = V_INT(stack_topn(ctx, n));
|
||||
return V_DISPATCH(stack_topn(ctx, n+1));
|
||||
}
|
||||
|
||||
static void exprval_release(exprval_t *val)
|
||||
|
@ -1638,13 +1638,14 @@ static HRESULT interp_member(exec_ctx_t *ctx)
|
|||
/* ECMA-262 3rd Edition 11.2.1 */
|
||||
static HRESULT interp_memberid(exec_ctx_t *ctx)
|
||||
{
|
||||
const unsigned arg = ctx->parser->code->instrs[ctx->ip].arg1.lng;
|
||||
VARIANT *objv, *namev;
|
||||
IDispatch *obj;
|
||||
BSTR name;
|
||||
DISPID id;
|
||||
HRESULT hres;
|
||||
|
||||
TRACE("\n");
|
||||
TRACE("%x\n", arg);
|
||||
|
||||
namev = stack_pop(ctx);
|
||||
objv = stack_pop(ctx);
|
||||
|
@ -1660,12 +1661,17 @@ static HRESULT interp_memberid(exec_ctx_t *ctx)
|
|||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
hres = disp_get_id(ctx->parser->script, obj, name, fdexNameEnsure, &id);
|
||||
hres = disp_get_id(ctx->parser->script, obj, name, arg, &id);
|
||||
SysFreeString(name);
|
||||
if(FAILED(hres)) {
|
||||
IDispatch_Release(obj);
|
||||
if(hres == DISP_E_UNKNOWNNAME && !(arg & fdexNameEnsure)) {
|
||||
obj = NULL;
|
||||
id = JS_E_INVALID_PROPERTY;
|
||||
}else {
|
||||
return hres;
|
||||
}
|
||||
}
|
||||
|
||||
return stack_push_objid(ctx, obj, id);
|
||||
}
|
||||
|
@ -1680,7 +1686,7 @@ static HRESULT interp_refval(exec_ctx_t *ctx)
|
|||
|
||||
TRACE("\n");
|
||||
|
||||
disp = stack_top_objid(ctx, &id);
|
||||
disp = stack_topn_objid(ctx, 0, &id);
|
||||
if(!disp)
|
||||
return throw_reference_error(ctx->parser->script, &ctx->ei, JS_E_ILLEGAL_ASSIGN, NULL);
|
||||
|
||||
|
@ -1817,9 +1823,7 @@ HRESULT call_expression_eval(script_ctx_t *ctx, expression_t *_expr, DWORD flags
|
|||
hres = throw_type_error(ctx, ei, JS_E_INVALID_PROPERTY, NULL);
|
||||
break;
|
||||
case EXPRVAL_IDREF:
|
||||
hres = disp_call(ctx, exprval.u.idref.disp, exprval.u.idref.id,
|
||||
DISPATCH_METHOD, &dp, flags & EXPR_NOVAL ? NULL : &var, ei, NULL/*FIXME*/);
|
||||
break;
|
||||
assert(0);
|
||||
case EXPRVAL_INVALID:
|
||||
hres = throw_type_error(ctx, ei, JS_E_OBJECT_EXPECTED, NULL);
|
||||
break;
|
||||
|
@ -1845,6 +1849,33 @@ HRESULT call_expression_eval(script_ctx_t *ctx, expression_t *_expr, DWORD flags
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
/* ECMA-262 3rd Edition 11.2.3 */
|
||||
static HRESULT interp_call_member(exec_ctx_t *ctx)
|
||||
{
|
||||
const unsigned argn = ctx->parser->code->instrs[ctx->ip].arg1.uint;
|
||||
const int do_ret = ctx->parser->code->instrs[ctx->ip].arg2.lng;
|
||||
IDispatch *obj;
|
||||
DISPPARAMS dp;
|
||||
VARIANT v;
|
||||
DISPID id;
|
||||
HRESULT hres;
|
||||
|
||||
TRACE("%d %d\n", argn, do_ret);
|
||||
|
||||
obj = stack_topn_objid(ctx, argn, &id);
|
||||
if(!obj)
|
||||
return throw_type_error(ctx->parser->script, &ctx->ei, id, NULL);
|
||||
|
||||
jsstack_to_dp(ctx, argn, &dp);
|
||||
hres = disp_call(ctx->parser->script, obj, id, DISPATCH_METHOD, &dp, do_ret ? &v : NULL, &ctx->ei, NULL/*FIXME*/);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
stack_popn(ctx, argn+2);
|
||||
return do_ret ? stack_push(ctx, &v) : S_OK;
|
||||
|
||||
}
|
||||
|
||||
/* ECMA-262 3rd Edition 11.1.1 */
|
||||
static HRESULT interp_this(exec_ctx_t *ctx)
|
||||
{
|
||||
|
@ -1903,19 +1934,20 @@ static HRESULT interp_ident(exec_ctx_t *ctx)
|
|||
static HRESULT interp_identid(exec_ctx_t *ctx)
|
||||
{
|
||||
const BSTR arg = ctx->parser->code->instrs[ctx->ip].arg1.bstr;
|
||||
const unsigned flags = ctx->parser->code->instrs[ctx->ip].arg2.uint;
|
||||
exprval_t exprval;
|
||||
HRESULT hres;
|
||||
|
||||
TRACE("%s\n", debugstr_w(arg));
|
||||
TRACE("%s %x\n", debugstr_w(arg), flags);
|
||||
|
||||
hres = identifier_eval(ctx->parser->script, arg, EXPR_NEWREF, &ctx->ei, &exprval);
|
||||
hres = identifier_eval(ctx->parser->script, arg, (flags&fdexNameEnsure) ? EXPR_NEWREF : 0, &ctx->ei, &exprval);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
if(exprval.type != EXPRVAL_IDREF) {
|
||||
WARN("invalid ref\n");
|
||||
exprval_release(&exprval);
|
||||
return stack_push_objid(ctx, NULL, -1);
|
||||
return stack_push_objid(ctx, NULL, JS_E_OBJECT_EXPECTED);
|
||||
}
|
||||
|
||||
return stack_push_objid(ctx, exprval.u.idref.disp, exprval.u.idref.id);
|
||||
|
@ -3437,9 +3469,12 @@ static HRESULT interp_expression_eval(script_ctx_t *ctx, expression_t *expr, DWO
|
|||
return hres;
|
||||
}
|
||||
|
||||
assert(exec_ctx->top == prev_top+1);
|
||||
assert(exec_ctx->top == prev_top+1 || ((flags&EXPR_NOVAL) && exec_ctx->top == prev_top));
|
||||
|
||||
ret->type = EXPRVAL_VARIANT;
|
||||
if(exec_ctx->top == prev_top)
|
||||
V_VT(&ret->u.var) = VT_EMPTY;
|
||||
else
|
||||
ret->u.var = *stack_pop(exec_ctx);
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -3450,7 +3485,7 @@ HRESULT compiled_expression_eval(script_ctx_t *ctx, expression_t *expr, DWORD fl
|
|||
|
||||
TRACE("\n");
|
||||
|
||||
hres = compile_subscript(ctx->exec_ctx->parser, expr, &expr->instr_off);
|
||||
hres = compile_subscript(ctx->exec_ctx->parser, expr, !(flags & EXPR_NOVAL), &expr->instr_off);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ typedef struct _func_stack {
|
|||
X(assign, 1, 0,0) \
|
||||
X(bool, 1, ARG_INT, 0) \
|
||||
X(bneg, 1, 0,0) \
|
||||
X(call_member,1, ARG_UINT, ARG_UINT) \
|
||||
X(delete, 1, 0,0) \
|
||||
X(div, 1, 0,0) \
|
||||
X(double, 1, ARG_SBL, 0) \
|
||||
|
@ -54,7 +55,7 @@ typedef struct _func_stack {
|
|||
X(gt, 1, 0,0) \
|
||||
X(gteq, 1, 0,0) \
|
||||
X(ident, 1, ARG_BSTR, 0) \
|
||||
X(identid, 1, ARG_BSTR, 0) \
|
||||
X(identid, 1, ARG_BSTR, ARG_INT) \
|
||||
X(in, 1, 0,0) \
|
||||
X(int, 1, ARG_INT, 0) \
|
||||
X(jmp, 0, ARG_ADDR, 0) \
|
||||
|
@ -63,7 +64,7 @@ typedef struct _func_stack {
|
|||
X(lt, 1, 0,0) \
|
||||
X(lteq, 1, 0,0) \
|
||||
X(member, 1, ARG_BSTR, 0) \
|
||||
X(memberid, 1, 0,0) \
|
||||
X(memberid, 1, ARG_UINT, 0) \
|
||||
X(minus, 1, 0,0) \
|
||||
X(mod, 1, 0,0) \
|
||||
X(mul, 1, 0,0) \
|
||||
|
@ -580,4 +581,4 @@ HRESULT assign_and_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t*
|
|||
|
||||
HRESULT compiled_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*) DECLSPEC_HIDDEN;
|
||||
|
||||
HRESULT compile_subscript(parser_ctx_t*,expression_t*,unsigned*) DECLSPEC_HIDDEN;
|
||||
HRESULT compile_subscript(parser_ctx_t*,expression_t*,BOOL,unsigned*) DECLSPEC_HIDDEN;
|
||||
|
|
|
@ -1356,7 +1356,7 @@ static const expression_eval_t expression_eval_table[] = {
|
|||
array_expression_eval,
|
||||
member_expression_eval,
|
||||
compiled_expression_eval,
|
||||
call_expression_eval,
|
||||
compiled_expression_eval,
|
||||
compiled_expression_eval,
|
||||
function_expression_eval,
|
||||
identifier_expression_eval,
|
||||
|
|
|
@ -729,7 +729,7 @@ static HRESULT WINAPI Global_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid,
|
|||
|
||||
ok(wFlags == INVOKE_FUNC, "wFlags = %x\n", wFlags);
|
||||
ok(pdp != NULL, "pdp == NULL\n");
|
||||
todo_wine ok(pdp->rgvarg != NULL, "rgvarg == NULL\n");
|
||||
ok(pdp->rgvarg != NULL, "rgvarg == NULL\n");
|
||||
ok(!pdp->rgdispidNamedArgs, "rgdispidNamedArgs != NULL\n");
|
||||
ok(!pdp->cArgs, "cArgs = %d\n", pdp->cArgs);
|
||||
ok(!pdp->cNamedArgs, "cNamedArgs = %d\n", pdp->cNamedArgs);
|
||||
|
|
Loading…
Reference in New Issue