From b473f5d5c66f1d71dc8d884e196c54da75e522fe Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Fri, 18 Nov 2011 14:09:44 +0100 Subject: [PATCH] jscript: Added minimal bytecode compiler/interpreter and use it for '===' expressions. --- dlls/jscript/Makefile.in | 1 + dlls/jscript/compile.c | 144 +++++++++++++++++++++++++++++++++++++++++++ dlls/jscript/engine.c | 157 ++++++++++++++++++++++++++++++++++++++++++++--- dlls/jscript/engine.h | 54 +++++++++++++++- dlls/jscript/parser.y | 6 +- 5 files changed, 349 insertions(+), 13 deletions(-) create mode 100644 dlls/jscript/compile.c diff --git a/dlls/jscript/Makefile.in b/dlls/jscript/Makefile.in index 36268ab4164..665b64decbb 100644 --- a/dlls/jscript/Makefile.in +++ b/dlls/jscript/Makefile.in @@ -5,6 +5,7 @@ C_SRCS = \ activex.c \ array.c \ bool.c \ + compile.c \ date.c \ dispex.c \ engine.c \ diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c new file mode 100644 index 00000000000..c2f8eff5f74 --- /dev/null +++ b/dlls/jscript/compile.c @@ -0,0 +1,144 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +#include "jscript.h" +#include "engine.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(jscript); + +struct _compiler_ctx_t { + parser_ctx_t *parser; + bytecode_t *code; + + unsigned code_off; + unsigned code_size; +}; + +static HRESULT compile_expression(compiler_ctx_t*,expression_t*); + +static unsigned push_instr(compiler_ctx_t *ctx, jsop_t op) +{ + assert(ctx->code_size >= ctx->code_off); + + if(!ctx->code_size) { + ctx->code->instrs = heap_alloc(64 * sizeof(instr_t)); + if(!ctx->code->instrs) + return -1; + ctx->code_size = 64; + }else if(ctx->code_size == ctx->code_off) { + instr_t *new_instrs; + + new_instrs = heap_realloc(ctx->code->instrs, ctx->code_size*2*sizeof(instr_t)); + if(!new_instrs) + return -1; + + ctx->code->instrs = new_instrs; + ctx->code_size *= 2; + } + + ctx->code->instrs[ctx->code_off].op = op; + return ctx->code_off++; +} + +static inline instr_t *instr_ptr(compiler_ctx_t *ctx, unsigned off) +{ + assert(off < ctx->code_off); + return ctx->code->instrs + off; +} + +static HRESULT compile_binary_expression(compiler_ctx_t *ctx, binary_expression_t *expr, jsop_t op) +{ + HRESULT hres; + + hres = compile_expression(ctx, expr->expression1); + if(FAILED(hres)) + return hres; + + hres = compile_expression(ctx, expr->expression2); + if(FAILED(hres)) + return hres; + + return push_instr(ctx, op) == -1 ? E_OUTOFMEMORY : S_OK; +} + +static HRESULT compile_interp_fallback(compiler_ctx_t *ctx, expression_t *expr) +{ + unsigned instr; + + instr = push_instr(ctx, OP_tree); + if(instr == -1) + return E_OUTOFMEMORY; + + instr_ptr(ctx, instr)->arg1.expr = expr; + return S_OK; +} + +static HRESULT compile_expression(compiler_ctx_t *ctx, expression_t *expr) +{ + switch(expr->type) { + case EXPR_EQEQ: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_eq2); + default: + return compile_interp_fallback(ctx, expr); + } + + return S_OK; +} + +void release_bytecode(bytecode_t *code) +{ + heap_free(code->instrs); + heap_free(code); +} + +void release_compiler(compiler_ctx_t *ctx) +{ + heap_free(ctx); +} + +HRESULT compile_subscript(parser_ctx_t *parser, expression_t *expr, unsigned *ret_off) +{ + HRESULT hres; + + if(!parser->code) { + parser->code = heap_alloc_zero(sizeof(bytecode_t)); + if(!parser->code) + return E_OUTOFMEMORY; + } + + if(!parser->compiler) { + parser->compiler = heap_alloc_zero(sizeof(compiler_ctx_t)); + if(!parser->compiler) + return E_OUTOFMEMORY; + + parser->compiler->parser = parser; + parser->compiler->code = parser->code; + } + + *ret_off = parser->compiler->code_off; + hres = compile_expression(parser->compiler, expr); + if(FAILED(hres)) + return hres; + + return push_instr(parser->compiler, OP_ret) == -1 ? E_OUTOFMEMORY : S_OK; +} diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index 130fe41b5c1..102dadd4a4c 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -20,6 +20,7 @@ #include "wine/port.h" #include +#include #include "jscript.h" #include "engine.h" @@ -52,6 +53,51 @@ static inline HRESULT expr_eval(script_ctx_t *ctx, expression_t *expr, DWORD fla return expr->eval(ctx, expr, flags, ei, ret); } +static HRESULT stack_push(exec_ctx_t *ctx, VARIANT *v) +{ + if(!ctx->stack_size) { + ctx->stack = heap_alloc(16*sizeof(VARIANT)); + if(!ctx->stack) + return E_OUTOFMEMORY; + ctx->stack_size = 16; + }else if(ctx->stack_size == ctx->top) { + VARIANT *new_stack; + + new_stack = heap_realloc(ctx->stack, ctx->stack_size*2*sizeof(VARIANT)); + if(!new_stack) { + VariantClear(v); + return E_OUTOFMEMORY; + } + + ctx->stack = new_stack; + ctx->stack_size *= 2; + } + + ctx->stack[ctx->top++] = *v; + return S_OK; +} + +static HRESULT stack_push_bool(exec_ctx_t *ctx, BOOL b) +{ + VARIANT v; + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = b ? VARIANT_TRUE : VARIANT_FALSE; + return stack_push(ctx, &v); +} + +static inline VARIANT *stack_pop(exec_ctx_t *ctx) +{ + assert(ctx->top); + return ctx->stack + --ctx->top; +} + +static void stack_popn(exec_ctx_t *ctx, unsigned n) +{ + while(n--) + VariantClear(stack_pop(ctx)); +} + static void exprval_release(exprval_t *val) { switch(val->type) { @@ -231,6 +277,7 @@ void exec_release(exec_ctx_t *ctx) jsdisp_release(ctx->var_disp); if(ctx->this_obj) IDispatch_Release(ctx->this_obj); + heap_free(ctx->stack); heap_free(ctx); } @@ -2773,26 +2820,24 @@ HRESULT equal_expression_eval(script_ctx_t *ctx, expression_t *_expr, DWORD flag } /* ECMA-262 3rd Edition 11.9.4 */ -HRESULT equal2_expression_eval(script_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +static HRESULT interp_eq2(exec_ctx_t *ctx) { - binary_expression_t *expr = (binary_expression_t*)_expr; - VARIANT rval, lval; + VARIANT *l, *r; BOOL b; HRESULT hres; TRACE("\n"); - hres = get_binary_expr_values(ctx, expr, ei, &rval, &lval); - if(FAILED(hres)) - return hres; + r = stack_pop(ctx); + l = stack_pop(ctx); - hres = equal2_values(&rval, &lval, &b); - VariantClear(&lval); - VariantClear(&rval); + hres = equal2_values(r, l, &b); + VariantClear(l); + VariantClear(r); if(FAILED(hres)) return hres; - return return_bool(ret, b); + return stack_push_bool(ctx, b); } /* ECMA-262 3rd Edition 11.9.2 */ @@ -3261,3 +3306,95 @@ HRESULT assign_xor_expression_eval(script_ctx_t *ctx, expression_t *_expr, DWORD return assign_oper_eval(ctx, expr->expression1, expr->expression2, xor_eval, ei, ret); } + +static HRESULT interp_ret(exec_ctx_t *ctx) +{ + TRACE("\n"); + + ctx->ip = -1; + return S_OK; +} + +static HRESULT interp_tree(exec_ctx_t *ctx) +{ + instr_t *instr = ctx->parser->code->instrs+ctx->ip; + exprval_t val; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx->parser->script, instr->arg1.expr, 0, &ctx->ei, &val); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &val, &ctx->ei, &v); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +typedef HRESULT (*op_func_t)(exec_ctx_t*); + +static const op_func_t op_funcs[] = { +#define X(x,a,b) interp_##x, +OP_LIST +#undef X +}; + +static const unsigned op_move[] = { +#define X(a,x,b) x, +OP_LIST +#undef X +}; + +HRESULT interp_expression_eval(script_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +{ + exec_ctx_t *exec_ctx = ctx->exec_ctx; + unsigned prev_ip, prev_top; + jsop_t op; + HRESULT hres = S_OK; + + TRACE("\n"); + + prev_top = exec_ctx->top; + prev_ip = exec_ctx->ip; + exec_ctx->ip = expr->instr_off; + + while(exec_ctx->ip != -1) { + op = exec_ctx->parser->code->instrs[exec_ctx->ip].op; + hres = op_funcs[op](exec_ctx); + if(FAILED(hres)) + break; + exec_ctx->ip += op_move[op]; + } + + exec_ctx->ip = prev_ip; + + if(FAILED(hres)) { + stack_popn(exec_ctx, exec_ctx->top-prev_top); + *ei = exec_ctx->ei; + memset(&exec_ctx->ei, 0, sizeof(exec_ctx->ei)); + return hres; + } + + assert(exec_ctx->top == prev_top+1); + + ret->type = EXPRVAL_VARIANT; + ret->u.var = *stack_pop(exec_ctx); + return S_OK; +} + +HRESULT compiled_expression_eval(script_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +{ + HRESULT hres; + + TRACE("\n"); + + hres = compile_subscript(ctx->exec_ctx->parser, expr, &expr->instr_off); + if(FAILED(hres)) + return hres; + + return (expr->eval = interp_expression_eval)(ctx, expr, flags, ei, ret); +} diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index c346e5f8f32..09f6e11b9d1 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -18,6 +18,7 @@ typedef struct _source_elements_t source_elements_t; typedef struct _function_expression_t function_expression_t; +typedef struct _expression_t expression_t; typedef struct _function_declaration_t { function_expression_t *expr; @@ -40,6 +41,42 @@ typedef struct _func_stack { struct _func_stack *next; } func_stack_t; +#define OP_LIST \ + X(eq2, 1, 0) \ + X(tree, 1, ARG_EXPR) \ + X(ret, 0, 0) + +typedef enum { +#define X(x,a,b) OP_##x, +OP_LIST +#undef X + OP_LAST +} jsop_t; + +typedef union { + expression_t *expr; +} instr_arg_t; + +typedef enum { + ARG_NONE = 0, + ARG_EXPR +} instr_arg_type_t; + +typedef struct { + jsop_t op; + instr_arg_t arg1; +} instr_t; + +typedef struct { + instr_t *instrs; +} bytecode_t; + +void release_bytecode(bytecode_t*); + +typedef struct _compiler_ctx_t compiler_ctx_t; + +void release_compiler(compiler_ctx_t*); + typedef struct _parser_ctx_t { LONG ref; @@ -58,6 +95,9 @@ typedef struct _parser_ctx_t { func_stack_t *func_stack; + bytecode_t *code; + compiler_ctx_t *compiler; + struct _parser_ctx_t *next; } parser_ctx_t; @@ -103,6 +143,13 @@ struct _exec_ctx_t { jsdisp_t *var_disp; IDispatch *this_obj; BOOL is_global; + + VARIANT *stack; + unsigned stack_size; + unsigned top; + + unsigned ip; + jsexcept_t ei; }; static inline void exec_addref(exec_ctx_t *ctx) @@ -115,7 +162,6 @@ HRESULT create_exec_ctx(script_ctx_t*,IDispatch*,jsdisp_t*,scope_chain_t*,BOOL,e HRESULT exec_source(exec_ctx_t*,parser_ctx_t*,source_elements_t*,BOOL,jsexcept_t*,VARIANT*) DECLSPEC_HIDDEN; typedef struct _statement_t statement_t; -typedef struct _expression_t expression_t; typedef struct _parameter_t parameter_t; HRESULT create_source_function(parser_ctx_t*,parameter_t*,source_elements_t*,scope_chain_t*, @@ -357,6 +403,7 @@ typedef HRESULT (*expression_eval_t)(script_ctx_t*,expression_t*,DWORD,jsexcept_ struct _expression_t { expression_type_t type; expression_eval_t eval; + unsigned instr_off; }; struct _parameter_t { @@ -493,7 +540,6 @@ HRESULT post_decrement_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcep HRESULT pre_increment_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*) DECLSPEC_HIDDEN; HRESULT pre_decrement_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*) DECLSPEC_HIDDEN; HRESULT equal_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*) DECLSPEC_HIDDEN; -HRESULT equal2_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*) DECLSPEC_HIDDEN; HRESULT not_equal_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*) DECLSPEC_HIDDEN; HRESULT not_equal2_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*) DECLSPEC_HIDDEN; HRESULT less_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*) DECLSPEC_HIDDEN; @@ -517,3 +563,7 @@ HRESULT assign_mod_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t* HRESULT assign_and_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*) DECLSPEC_HIDDEN; HRESULT assign_or_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*) DECLSPEC_HIDDEN; HRESULT assign_xor_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*) DECLSPEC_HIDDEN; + +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; diff --git a/dlls/jscript/parser.y b/dlls/jscript/parser.y index db7a3c58c08..444871b00f2 100644 --- a/dlls/jscript/parser.y +++ b/dlls/jscript/parser.y @@ -1328,7 +1328,7 @@ static const expression_eval_t expression_eval_table[] = { pre_increment_expression_eval, pre_decrement_expression_eval, equal_expression_eval, - equal2_expression_eval, + compiled_expression_eval, not_equal_expression_eval, not_equal2_expression_eval, less_expression_eval, @@ -1574,6 +1574,10 @@ void parser_release(parser_ctx_t *ctx) if(--ctx->ref) return; + if(ctx->code) + release_bytecode(ctx->code); + if(ctx->compiler) + release_compiler(ctx->compiler); script_release(ctx->script); heap_free(ctx->begin); jsheap_free(&ctx->heap); -- 2.11.4.GIT