jsdisp_t *jsobj;
HRESULT hres;
- jsobj = iface_to_jsdisp((IUnknown*)obj);
+ jsobj = iface_to_jsdisp(obj);
if(jsobj) {
if(is_class(jsobj, JSCLASS_ARRAY)) {
hres = concat_array(array, (ArrayInstance*)jsobj, len);
return E_FAIL;
}
- cmp_func = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
+ cmp_func = iface_to_jsdisp(get_object(argv[0]));
if(!cmp_func || !is_class(cmp_func, JSCLASS_FUNCTION)) {
WARN("cmp_func is not a function\n");
if(cmp_func)
unsigned labels_size;
unsigned labels_cnt;
+ local_ref_t *locals_buf;
+ unsigned locals_buf_size;
+ unsigned locals_cnt;
+
statement_ctx_t *stat_ctx;
function_code_t *func;
- variable_declaration_t *var_head;
- variable_declaration_t *var_tail;
-
function_expression_t *func_head;
function_expression_t *func_tail;
} compiler_ctx_t;
return type == EXPR_IDENT || type == EXPR_MEMBER || type == EXPR_ARRAY;
}
+static BOOL bind_local(compiler_ctx_t *ctx, const WCHAR *identifier, int *ret_ref)
+{
+ statement_ctx_t *iter;
+ local_ref_t *ref;
+
+ for(iter = ctx->stat_ctx; iter; iter = iter->next) {
+ if(iter->using_scope)
+ return FALSE;
+ }
+
+ ref = lookup_local(ctx->func, identifier);
+ if(!ref)
+ return FALSE;
+
+ *ret_ref = ref->ref;
+ return TRUE;
+}
+
+static HRESULT emit_identifier_ref(compiler_ctx_t *ctx, const WCHAR *identifier, unsigned flags)
+{
+ int local_ref;
+ if(bind_local(ctx, identifier, &local_ref))
+ return push_instr_int(ctx, OP_local_ref, local_ref);
+ return push_instr_bstr_uint(ctx, OP_identid, identifier, flags);
+}
+
+static HRESULT emit_identifier(compiler_ctx_t *ctx, const WCHAR *identifier)
+{
+ int local_ref;
+ if(bind_local(ctx, identifier, &local_ref))
+ return push_instr_int(ctx, OP_local, local_ref);
+ return push_instr_bstr(ctx, OP_ident, identifier);
+}
+
static HRESULT compile_memberid_expression(compiler_ctx_t *ctx, expression_t *expr, unsigned flags)
{
HRESULT hres = S_OK;
case EXPR_IDENT: {
identifier_expression_t *ident_expr = (identifier_expression_t*)expr;
- hres = push_instr_bstr_uint(ctx, OP_identid, ident_expr->identifier, flags);
+ hres = emit_identifier_ref(ctx, ident_expr->identifier, flags);
break;
}
case EXPR_ARRAY: {
static HRESULT compile_function_expression(compiler_ctx_t *ctx, function_expression_t *expr, BOOL emit_ret)
{
- unsigned func_id = ctx->func->func_cnt++;
- ctx->func_tail = ctx->func_tail ? (ctx->func_tail->next = expr) : (ctx->func_head = expr);
- return emit_ret ? push_instr_uint(ctx, OP_func, func_id) : S_OK;
+ return emit_ret ? push_instr_uint(ctx, OP_func, expr->func_id) : S_OK;
}
static HRESULT compile_expression(compiler_ctx_t *ctx, expression_t *expr, BOOL emit_ret)
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_gteq);
break;
case EXPR_IDENT:
- hres = push_instr_bstr(ctx, OP_ident, ((identifier_expression_t*)expr)->identifier);
+ hres = emit_identifier(ctx, ((identifier_expression_t*)expr)->identifier);
break;
case EXPR_IN:
hres = compile_binary_expression(ctx, (binary_expression_t*)expr, OP_in);
assert(list != NULL);
- if(ctx->var_tail)
- ctx->var_tail->global_next = list;
- else
- ctx->var_head = list;
-
for(iter = list; iter; iter = iter->next) {
- ctx->func->var_cnt++;
- iter->global_next = iter->next;
- if(!iter->next)
- ctx->var_tail = iter;
-
if(!iter->expr)
continue;
+ hres = emit_identifier_ref(ctx, iter->identifier, 0);
+ if(FAILED(hres))
+ return hres;
+
hres = compile_expression(ctx, iter->expr, TRUE);
if(FAILED(hres))
return hres;
- hres = push_instr_bstr(ctx, OP_var_set, iter->identifier);
+ if(!push_instr(ctx, OP_assign))
+ return E_OUTOFMEMORY;
+
+ hres = push_instr_uint(ctx, OP_pop, 1);
if(FAILED(hres))
return hres;
}
return hres;
if(stat->variable) {
- hres = push_instr_bstr_uint(ctx, OP_identid, stat->variable->identifier, fdexNameEnsure);
+ hres = emit_identifier_ref(ctx, stat->variable->identifier, fdexNameEnsure);
if(FAILED(hres))
return hres;
}else if(is_memberid_expr(stat->expr->type)) {
return hres;
}
+static int local_cmp(const void *key, const void *ref)
+{
+ return strcmpW((const WCHAR*)key, ((const local_ref_t*)ref)->name);
+}
+
+static inline local_ref_t *find_local(compiler_ctx_t *ctx, const WCHAR *name)
+{
+ return bsearch(name, ctx->locals_buf, ctx->locals_cnt, sizeof(*ctx->locals_buf), local_cmp);
+}
+
+static BOOL alloc_local(compiler_ctx_t *ctx, BSTR name, int ref)
+{
+ unsigned i;
+
+ if(!ctx->locals_buf_size) {
+ ctx->locals_buf = heap_alloc(4 * sizeof(*ctx->locals_buf));
+ if(!ctx->locals_buf)
+ return FALSE;
+ ctx->locals_buf_size = 4;
+ }else if(ctx->locals_buf_size == ctx->locals_cnt) {
+ local_ref_t *new_buf = heap_realloc(ctx->locals_buf, ctx->locals_buf_size * 2 * sizeof(*ctx->locals_buf));
+ if(!new_buf)
+ return FALSE;
+ ctx->locals_buf = new_buf;
+ ctx->locals_buf_size *= 2;
+ }
+
+ for(i = 0; i < ctx->locals_cnt; i++) {
+ if(strcmpW(ctx->locals_buf[i].name, name) > 0) {
+ memmove(ctx->locals_buf + i+1, ctx->locals_buf + i, (ctx->locals_cnt - i) * sizeof(*ctx->locals_buf));
+ break;
+ }
+ }
+
+ ctx->locals_buf[i].name = name;
+ ctx->locals_buf[i].ref = ref;
+ ctx->locals_cnt++;
+ return TRUE;
+}
+
+static BOOL alloc_variable(compiler_ctx_t *ctx, const WCHAR *name)
+{
+ BSTR ident;
+
+ if(find_local(ctx, name))
+ return TRUE;
+
+ ident = compiler_alloc_bstr(ctx, name);
+ if(!ident)
+ return FALSE;
+
+ return alloc_local(ctx, ident, ctx->func->var_cnt++);
+}
+
+static BOOL visit_function_expression(compiler_ctx_t *ctx, function_expression_t *expr)
+{
+ expr->func_id = ctx->func->func_cnt++;
+ ctx->func_tail = ctx->func_tail ? (ctx->func_tail->next = expr) : (ctx->func_head = expr);
+
+ return !expr->identifier || expr->event_target || alloc_variable(ctx, expr->identifier);
+}
+
+static HRESULT visit_expression(compiler_ctx_t *ctx, expression_t *expr)
+{
+ HRESULT hres = S_OK;
+
+ switch(expr->type) {
+ case EXPR_ADD:
+ case EXPR_AND:
+ case EXPR_ARRAY:
+ case EXPR_ASSIGN:
+ case EXPR_ASSIGNADD:
+ case EXPR_ASSIGNAND:
+ case EXPR_ASSIGNSUB:
+ case EXPR_ASSIGNMUL:
+ case EXPR_ASSIGNDIV:
+ case EXPR_ASSIGNMOD:
+ case EXPR_ASSIGNOR:
+ case EXPR_ASSIGNLSHIFT:
+ case EXPR_ASSIGNRSHIFT:
+ case EXPR_ASSIGNRRSHIFT:
+ case EXPR_ASSIGNXOR:
+ case EXPR_BAND:
+ case EXPR_BOR:
+ case EXPR_COMMA:
+ case EXPR_DIV:
+ case EXPR_EQ:
+ case EXPR_EQEQ:
+ case EXPR_GREATER:
+ case EXPR_GREATEREQ:
+ case EXPR_IN:
+ case EXPR_INSTANCEOF:
+ case EXPR_LESS:
+ case EXPR_LESSEQ:
+ case EXPR_LSHIFT:
+ case EXPR_MOD:
+ case EXPR_MUL:
+ case EXPR_NOTEQ:
+ case EXPR_NOTEQEQ:
+ case EXPR_OR:
+ case EXPR_RSHIFT:
+ case EXPR_RRSHIFT:
+ case EXPR_SUB:
+ case EXPR_BXOR: {
+ binary_expression_t *binary_expr = (binary_expression_t*)expr;
+
+ hres = visit_expression(ctx, binary_expr->expression1);
+ if(FAILED(hres))
+ return hres;
+
+ hres = visit_expression(ctx, binary_expr->expression2);
+ break;
+ }
+ case EXPR_BITNEG:
+ case EXPR_DELETE:
+ case EXPR_LOGNEG:
+ case EXPR_MINUS:
+ case EXPR_PLUS:
+ case EXPR_POSTDEC:
+ case EXPR_POSTINC:
+ case EXPR_PREDEC:
+ case EXPR_PREINC:
+ case EXPR_TYPEOF:
+ case EXPR_VOID:
+ hres = visit_expression(ctx, ((unary_expression_t*)expr)->expression);
+ break;
+ case EXPR_IDENT:
+ case EXPR_LITERAL:
+ case EXPR_THIS:
+ break;
+ case EXPR_ARRAYLIT: {
+ array_literal_expression_t *array_expr = (array_literal_expression_t*)expr;
+ array_element_t *iter;
+
+ for(iter = array_expr->element_list; iter; iter = iter->next) {
+ hres = visit_expression(ctx, iter->expr);
+ if(FAILED(hres))
+ return hres;
+ }
+ break;
+ }
+ case EXPR_CALL:
+ case EXPR_NEW: {
+ call_expression_t *call_expr = (call_expression_t*)expr;
+ argument_t *arg;
+
+ hres = visit_expression(ctx, call_expr->expression);
+ if(FAILED(hres))
+ return hres;
+
+ for(arg = call_expr->argument_list; arg; arg = arg->next) {
+ hres = visit_expression(ctx, arg->expr);
+ if(FAILED(hres))
+ return hres;
+ }
+ break;
+ }
+ case EXPR_COND: {
+ conditional_expression_t *cond_expr = (conditional_expression_t*)expr;
+
+ hres = visit_expression(ctx, cond_expr->expression);
+ if(FAILED(hres))
+ return hres;
+
+ hres = visit_expression(ctx, cond_expr->true_expression);
+ if(FAILED(hres))
+ return hres;
+
+ hres = visit_expression(ctx, cond_expr->false_expression);
+ break;
+ }
+ case EXPR_FUNC:
+ visit_function_expression(ctx, (function_expression_t*)expr);
+ break;
+ case EXPR_MEMBER:
+ hres = visit_expression(ctx, ((member_expression_t*)expr)->expression);
+ break;
+ case EXPR_PROPVAL: {
+ prop_val_t *iter;
+ for(iter = ((property_value_expression_t*)expr)->property_list; iter; iter = iter->next) {
+ hres = visit_expression(ctx, iter->value);
+ if(FAILED(hres))
+ return hres;
+ }
+ break;
+ }
+ DEFAULT_UNREACHABLE;
+ }
+
+ return hres;
+}
+
+static HRESULT visit_variable_list(compiler_ctx_t *ctx, variable_declaration_t *list)
+{
+ variable_declaration_t *iter;
+ HRESULT hres;
+
+ for(iter = list; iter; iter = iter->next) {
+ if(!alloc_variable(ctx, iter->identifier))
+ return E_OUTOFMEMORY;
+
+ if(iter->expr) {
+ hres = visit_expression(ctx, iter->expr);
+ if(FAILED(hres))
+ return hres;
+ }
+ }
+
+ return S_OK;
+}
+
+static HRESULT visit_statement(compiler_ctx_t*,statement_t*);
+
+static HRESULT visit_block_statement(compiler_ctx_t *ctx, statement_t *iter)
+{
+ HRESULT hres;
+
+ while(iter) {
+ hres = visit_statement(ctx, iter);
+ if(FAILED(hres))
+ return hres;
+
+ iter = iter->next;
+ }
+
+ return S_OK;
+}
+
+static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat)
+{
+ HRESULT hres = S_OK;
+
+ switch(stat->type) {
+ case STAT_BLOCK:
+ hres = visit_block_statement(ctx, ((block_statement_t*)stat)->stat_list);
+ break;
+ case STAT_BREAK:
+ case STAT_CONTINUE:
+ case STAT_EMPTY:
+ break;
+ case STAT_EXPR:
+ case STAT_RETURN:
+ case STAT_THROW: {
+ expression_statement_t *expr_stat = (expression_statement_t*)stat;
+ if(expr_stat->expr)
+ hres = visit_expression(ctx, expr_stat->expr);
+ break;
+ }
+ case STAT_FOR: {
+ for_statement_t *for_stat = (for_statement_t*)stat;
+
+ if(for_stat->variable_list)
+ hres = visit_variable_list(ctx, for_stat->variable_list);
+ else if(for_stat->begin_expr)
+ hres = visit_expression(ctx, for_stat->begin_expr);
+ if(FAILED(hres))
+ break;
+
+ if(for_stat->expr) {
+ hres = visit_expression(ctx, for_stat->expr);
+ if(FAILED(hres))
+ break;
+ }
+
+ hres = visit_statement(ctx, for_stat->statement);
+ if(FAILED(hres))
+ break;
+
+ if(for_stat->end_expr)
+ hres = visit_expression(ctx, for_stat->end_expr);
+ break;
+ }
+ case STAT_FORIN: {
+ forin_statement_t *forin_stat = (forin_statement_t*)stat;
+
+ if(forin_stat->variable) {
+ hres = visit_variable_list(ctx, forin_stat->variable);
+ if(FAILED(hres))
+ break;
+ }
+
+ hres = visit_expression(ctx, forin_stat->in_expr);
+ if(FAILED(hres))
+ return hres;
+
+ if(forin_stat->expr) {
+ hres = visit_expression(ctx, forin_stat->expr);
+ if(FAILED(hres))
+ return hres;
+ }
+
+ hres = visit_statement(ctx, forin_stat->statement);
+ break;
+ }
+ case STAT_IF: {
+ if_statement_t *if_stat = (if_statement_t*)stat;
+
+ hres = visit_expression(ctx, if_stat->expr);
+ if(FAILED(hres))
+ return hres;
+
+ hres = visit_statement(ctx, if_stat->if_stat);
+ if(FAILED(hres))
+ return hres;
+
+ if(if_stat->else_stat)
+ hres = visit_statement(ctx, if_stat->else_stat);
+ break;
+ }
+ case STAT_LABEL:
+ hres = visit_statement(ctx, ((labelled_statement_t*)stat)->statement);
+ break;
+ case STAT_SWITCH: {
+ switch_statement_t *switch_stat = (switch_statement_t*)stat;
+ statement_t *stat_iter;
+ case_clausule_t *iter;
+
+ hres = visit_expression(ctx, switch_stat->expr);
+ if(FAILED(hres))
+ return hres;
+
+ for(iter = switch_stat->case_list; iter; iter = iter->next) {
+ if(!iter->expr)
+ continue;
+ hres = visit_expression(ctx, iter->expr);
+ if(FAILED(hres))
+ return hres;
+ }
+
+ for(iter = switch_stat->case_list; iter; iter = iter->next) {
+ while(iter->next && iter->next->stat == iter->stat)
+ iter = iter->next;
+ for(stat_iter = iter->stat; stat_iter && (!iter->next || iter->next->stat != stat_iter);
+ stat_iter = stat_iter->next) {
+ hres = visit_statement(ctx, stat_iter);
+ if(FAILED(hres))
+ return hres;
+ }
+ }
+ break;
+ }
+ case STAT_TRY: {
+ try_statement_t *try_stat = (try_statement_t*)stat;
+
+ hres = visit_statement(ctx, try_stat->try_statement);
+ if(FAILED(hres))
+ return hres;
+
+ if(try_stat->catch_block) {
+ hres = visit_statement(ctx, try_stat->catch_block->statement);
+ if(FAILED(hres))
+ return hres;
+ }
+
+ if(try_stat->finally_statement)
+ hres = visit_statement(ctx, try_stat->finally_statement);
+ break;
+ }
+ case STAT_VAR:
+ hres = visit_variable_list(ctx, ((var_statement_t*)stat)->variable_list);
+ break;
+ case STAT_WHILE: {
+ while_statement_t *while_stat = (while_statement_t*)stat;
+
+ hres = visit_expression(ctx, while_stat->expr);
+ if(FAILED(hres))
+ return hres;
+
+ hres = visit_statement(ctx, while_stat->statement);
+ break;
+ }
+ case STAT_WITH: {
+ with_statement_t *with_stat = (with_statement_t*)stat;
+
+ hres = visit_expression(ctx, with_stat->expr);
+ if(FAILED(hres))
+ return hres;
+
+ hres = visit_statement(ctx, with_stat->statement);
+ break;
+ }
+ DEFAULT_UNREACHABLE;
+ }
+
+ return hres;
+}
+
static void resolve_labels(compiler_ctx_t *ctx, unsigned off)
{
instr_t *instr;
static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source, function_expression_t *func_expr,
BOOL from_eval, function_code_t *func)
{
- variable_declaration_t *var_iter;
function_expression_t *iter;
- unsigned off, i;
+ unsigned off, i, j;
HRESULT hres;
TRACE("\n");
- ctx->var_head = ctx->var_tail = NULL;
ctx->func_head = ctx->func_tail = NULL;
ctx->from_eval = from_eval;
-
- off = ctx->code_off;
ctx->func = func;
- hres = compile_block_statement(ctx, source->statement);
- if(FAILED(hres))
- return hres;
-
- resolve_labels(ctx, off);
-
- hres = push_instr_uint(ctx, OP_ret, !from_eval);
- if(FAILED(hres))
- return hres;
-
- if(TRACE_ON(jscript_disas))
- dump_code(ctx, off);
-
- func->instr_off = off;
+ ctx->locals_cnt = 0;
if(func_expr) {
+ parameter_t *param_iter;
+
if(func_expr->identifier) {
func->name = compiler_alloc_bstr(ctx, func_expr->identifier);
if(!func->name)
if(!func->event_target)
return E_OUTOFMEMORY;
}
- }
-
- if(func_expr) {
- parameter_t *param_iter;
func->source = func_expr->src_str;
func->source_len = func_expr->src_len;
}
}
+ for(i = 0; i < func->param_cnt; i++) {
+ if(!find_local(ctx, func->params[i]) && !alloc_local(ctx, func->params[i], -i-1))
+ return E_OUTOFMEMORY;
+ }
+
+ hres = visit_block_statement(ctx, source->statement);
+ if(FAILED(hres))
+ return hres;
+
+ func->locals = compiler_alloc(ctx->code, ctx->locals_cnt * sizeof(*func->locals));
+ if(!func->locals)
+ return E_OUTOFMEMORY;
+ func->locals_cnt = ctx->locals_cnt;
+ memcpy(func->locals, ctx->locals_buf, func->locals_cnt * sizeof(*func->locals));
+
func->variables = compiler_alloc(ctx->code, func->var_cnt * sizeof(*func->variables));
if(!func->variables)
return E_OUTOFMEMORY;
- for(var_iter = ctx->var_head, i=0; var_iter; var_iter = var_iter->global_next, i++) {
- func->variables[i] = compiler_alloc_bstr(ctx, var_iter->identifier);
- if(!func->variables[i])
- return E_OUTOFMEMORY;
+ for(i = 0, j = 0; i < func->locals_cnt; i++) {
+ if(func->locals[i].ref < 0)
+ continue; /* skip arguments */
+ func->variables[func->locals[i].ref].name = func->locals[i].name;
+ func->variables[func->locals[i].ref].func_id = -1;
+ j++;
}
- assert(i == func->var_cnt);
+ assert(j == func->var_cnt);
func->funcs = compiler_alloc(ctx->code, func->func_cnt * sizeof(*func->funcs));
if(!func->funcs)
return E_OUTOFMEMORY;
memset(func->funcs, 0, func->func_cnt * sizeof(*func->funcs));
+ off = ctx->code_off;
+ hres = compile_block_statement(ctx, source->statement);
+ if(FAILED(hres))
+ return hres;
+
+ resolve_labels(ctx, off);
+
+ hres = push_instr_uint(ctx, OP_ret, !from_eval);
+ if(FAILED(hres))
+ return hres;
+
+ if(TRACE_ON(jscript_disas))
+ dump_code(ctx, off);
+
+ func->instr_off = off;
+
for(iter = ctx->func_head, i=0; iter; iter = iter->next, i++) {
hres = compile_function(ctx, iter->source_elements, iter, FALSE, func->funcs+i);
if(FAILED(hres))
return hres;
+
+ TRACE("[%d] func %s\n", i, debugstr_w(func->funcs[i].name));
+ if(func->funcs[i].name && !func->funcs[i].event_target) {
+ local_ref_t *local_ref = lookup_local(func, func->funcs[i].name);
+ func->funcs[i].local_ref = local_ref->ref;
+ TRACE("found ref %s %d for %s\n", debugstr_w(local_ref->name), local_ref->ref, debugstr_w(func->funcs[i].name));
+ if(local_ref->ref >= 0)
+ func->variables[local_ref->ref].func_id = i;
+ }
}
assert(i == func->func_cnt);
hres = compile_function(&compiler, compiler.parser->source, NULL, from_eval, &compiler.code->global_code);
parser_release(compiler.parser);
+ heap_free(compiler.locals_buf);
if(FAILED(hres)) {
release_bytecode(compiler.code);
return hres;
#include "jscript.h"
-/*
- * This IID is used to get jsdisp_t objecto from interface.
- * We might consider using private interface instead.
- */
-static const IID IID_IDispatchJS =
- {0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa6}};
-
#define FDEX_VERSION_MASK 0xf0000000
#define GOLDEN_RATIO 0x9E3779B9U
TRACE("%s = %s\n", debugstr_w(prop->name), debugstr_jsval(val));
hres = jsval_copy(val, &prop->u.val);
- if(FAILED(hres)) {
- prop->u.val = jsval_undefined();
+ if(FAILED(hres))
return hres;
- }
if(This->builtin_info->on_put)
This->builtin_info->on_put(This, prop->name);
}else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
*ppv = &This->IDispatchEx_iface;
- }else if(IsEqualGUID(&IID_IDispatchJS, riid)) {
- TRACE("(%p)->(IID_IDispatchJS %p)\n", This, ppv);
- jsdisp_addref(This);
- *ppv = This;
- return S_OK;
}else {
WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
*ppv = NULL;
{
jsdisp_t *This = impl_from_IDispatchEx(iface);
ULONG ref = --This->ref;
+ TRACE("(%p) ref=%d\n", This, ref);
if(!ref)
jsdisp_free(This);
return ref;
}
if(is_object_instance(val))
- prot = iface_to_jsdisp((IUnknown*)get_object(val));
+ prot = iface_to_jsdisp(get_object(val));
jsval_release(val);
}
return hres;
}
-jsdisp_t *iface_to_jsdisp(IUnknown *iface)
+jsdisp_t *iface_to_jsdisp(IDispatch *iface)
{
- jsdisp_t *ret;
- HRESULT hres;
-
- hres = IUnknown_QueryInterface(iface, &IID_IDispatchJS, (void**)&ret);
- if(FAILED(hres))
- return NULL;
-
- return ret;
+ return iface->lpVtbl == (const IDispatchVtbl*)&DispatchExVtbl
+ ? jsdisp_addref( impl_from_IDispatchEx((IDispatchEx*)iface))
+ : NULL;
}
HRESULT jsdisp_get_id(jsdisp_t *jsdisp, const WCHAR *name, DWORD flags, DISPID *id)
unsigned i;
HRESULT hres;
- jsdisp = iface_to_jsdisp((IUnknown*)disp);
+ jsdisp = iface_to_jsdisp(disp);
if(jsdisp) {
if(flags & DISPATCH_PROPERTYPUT) {
FIXME("disp_call(propput) on builtin object\n");
assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK)));
- jsdisp = iface_to_jsdisp((IUnknown*)disp);
+ jsdisp = iface_to_jsdisp(disp);
if(jsdisp) {
hres = jsdisp_call_value(jsdisp, jsthis, flags, argc, argv, r);
jsdisp_release(jsdisp);
jsdisp_t *jsdisp;
HRESULT hres;
- jsdisp = iface_to_jsdisp((IUnknown*)disp);
+ jsdisp = iface_to_jsdisp(disp);
if(jsdisp) {
dispex_prop_t *prop;
VARIANT var;
HRESULT hres;
- jsdisp = iface_to_jsdisp((IUnknown*)disp);
+ jsdisp = iface_to_jsdisp(disp);
if(jsdisp) {
hres = jsdisp_propget(jsdisp, id, val);
jsdisp_release(jsdisp);
jsdisp_t *jsdisp;
HRESULT hres;
- jsdisp = iface_to_jsdisp((IUnknown*)disp);
+ jsdisp = iface_to_jsdisp(disp);
if(jsdisp) {
dispex_prop_t *prop;
BSTR bstr;
HRESULT hres;
- jsdisp = iface_to_jsdisp((IUnknown*)disp);
+ jsdisp = iface_to_jsdisp(disp);
if(jsdisp) {
dispex_prop_t *prop;
const WCHAR *ptr;
enum {
EXPRVAL_JSVAL,
EXPRVAL_IDREF,
+ EXPRVAL_STACK_REF,
EXPRVAL_INVALID
} type;
union {
IDispatch *disp;
DISPID id;
} idref;
+ unsigned off;
+ HRESULT hres;
} u;
} exprval_t;
return stack_push(ctx, jsval_string(v));
}
-static HRESULT stack_push_objid(script_ctx_t *ctx, IDispatch *disp, DISPID id)
-{
- HRESULT hres;
-
- hres = stack_push(ctx, jsval_disp(disp));
- if(FAILED(hres))
- return hres;
-
- return stack_push(ctx, jsval_number(id));
-}
-
static inline jsval_t stack_top(script_ctx_t *ctx)
{
assert(ctx->stack_top > ctx->call_ctx->stack_base);
return ctx->stack[ctx->stack_top-1];
}
-static inline jsval_t stack_topn(script_ctx_t *ctx, unsigned n)
+static inline jsval_t *stack_top_ref(script_ctx_t *ctx, unsigned n)
{
assert(ctx->stack_top > ctx->call_ctx->stack_base+n);
- return ctx->stack[ctx->stack_top-1-n];
+ return ctx->stack+ctx->stack_top-1-n;
+}
+
+static inline jsval_t stack_topn(script_ctx_t *ctx, unsigned n)
+{
+ return *stack_top_ref(ctx, n);
}
static inline jsval_t *stack_args(script_ctx_t *ctx, unsigned n)
return to_uint32(ctx, stack_pop(ctx), r);
}
-static inline IDispatch *stack_pop_objid(script_ctx_t *ctx, DISPID *id)
+static inline unsigned local_off(call_frame_t *frame, int ref)
{
- assert(is_number(stack_top(ctx)) && is_object_instance(stack_topn(ctx, 1)));
+ return ref < 0
+ ? frame->arguments_off - ref-1
+ : frame->variables_off + ref;
+}
- *id = get_number(stack_pop(ctx));
- return get_object(stack_pop(ctx));
+static inline BSTR local_name(call_frame_t *frame, int ref)
+{
+ return ref < 0 ? frame->function->params[-ref-1] : frame->function->variables[ref].name;
}
-static inline IDispatch *stack_topn_objid(script_ctx_t *ctx, unsigned n, DISPID *id)
+/* Steals input reference even on failure. */
+static HRESULT stack_push_exprval(script_ctx_t *ctx, exprval_t *val)
{
- assert(is_number(stack_topn(ctx, n)) && is_object_instance(stack_topn(ctx, n+1)));
+ HRESULT hres;
+
+ switch(val->type) {
+ case EXPRVAL_JSVAL:
+ assert(0);
+ case EXPRVAL_IDREF:
+ hres = stack_push(ctx, jsval_disp(val->u.idref.disp));
+ if(SUCCEEDED(hres))
+ hres = stack_push(ctx, jsval_number(val->u.idref.id));
+ else
+ IDispatch_Release(val->u.idref.disp);
+ return hres;
+ case EXPRVAL_STACK_REF:
+ hres = stack_push(ctx, jsval_number(val->u.off));
+ if(SUCCEEDED(hres))
+ hres = stack_push(ctx, jsval_undefined());
+ return hres;
+ case EXPRVAL_INVALID:
+ hres = stack_push(ctx, jsval_undefined());
+ if(SUCCEEDED(hres))
+ hres = stack_push(ctx, jsval_number(val->u.hres));
+ return hres;
+ }
- *id = get_number(stack_topn(ctx, n));
- return get_object(stack_topn(ctx, n+1));
+ assert(0);
+ return E_FAIL;
}
-static inline jsval_t steal_ret(call_frame_t *frame)
+static BOOL stack_topn_exprval(script_ctx_t *ctx, unsigned n, exprval_t *r)
{
- jsval_t r = frame->ret;
- frame->ret = jsval_undefined();
- return r;
+ jsval_t v = stack_topn(ctx, n+1);
+
+ switch(jsval_type(v)) {
+ case JSV_NUMBER: {
+ call_frame_t *frame = ctx->call_ctx;
+ unsigned off = get_number(v);
+
+ if(!frame->base_scope->frame && off >= frame->arguments_off) {
+ DISPID id;
+ BSTR name;
+ HRESULT hres;
+
+ /* Got stack reference in deoptimized code. Need to convert it back to variable object reference. */
+
+ assert(off < frame->variables_off + frame->function->var_cnt);
+ name = off >= frame->variables_off
+ ? frame->function->variables[off - frame->variables_off].name
+ : frame->function->params[off - frame->arguments_off];
+ hres = jsdisp_get_id(ctx->call_ctx->base_scope->jsobj, name, 0, &id);
+ if(FAILED(hres)) {
+ r->type = EXPRVAL_INVALID;
+ r->u.hres = hres;
+ return FALSE;
+ }
+
+ *stack_top_ref(ctx, n+1) = jsval_obj(jsdisp_addref(frame->base_scope->jsobj));
+ *stack_top_ref(ctx, n) = jsval_number(id);
+ r->type = EXPRVAL_IDREF;
+ r->u.idref.disp = frame->base_scope->obj;
+ r->u.idref.id = id;
+ return TRUE;
+ }
+
+ r->type = EXPRVAL_STACK_REF;
+ r->u.off = off;
+ return TRUE;
+ }
+ case JSV_OBJECT:
+ r->type = EXPRVAL_IDREF;
+ r->u.idref.disp = get_object(v);
+ assert(is_number(stack_topn(ctx, n)));
+ r->u.idref.id = get_number(stack_topn(ctx, n));
+ return TRUE;
+ case JSV_UNDEFINED:
+ r->type = EXPRVAL_INVALID;
+ assert(is_number(stack_topn(ctx, n)));
+ r->u.hres = get_number(stack_topn(ctx, n));
+ return FALSE;
+ default:
+ assert(0);
+ return FALSE;
+ }
}
-static inline void clear_ret(call_frame_t *frame)
+static inline BOOL stack_pop_exprval(script_ctx_t *ctx, exprval_t *r)
{
- jsval_release(steal_ret(frame));
+ BOOL ret = stack_topn_exprval(ctx, 0, r);
+ ctx->stack_top -= 2;
+ return ret;
+}
+
+static HRESULT exprval_propput(script_ctx_t *ctx, exprval_t *ref, jsval_t v)
+{
+ switch(ref->type) {
+ case EXPRVAL_STACK_REF: {
+ jsval_t *r = ctx->stack + ref->u.off;
+ jsval_release(*r);
+ return jsval_copy(v, r);
+ }
+ case EXPRVAL_IDREF:
+ return disp_propput(ctx, ref->u.idref.disp, ref->u.idref.id, v);
+ default:
+ assert(0);
+ return E_FAIL;
+ }
+}
+
+static HRESULT exprval_propget(script_ctx_t *ctx, exprval_t *ref, jsval_t *r)
+{
+ switch(ref->type) {
+ case EXPRVAL_STACK_REF:
+ return jsval_copy(ctx->stack[ref->u.off], r);
+ case EXPRVAL_IDREF:
+ return disp_propget(ctx, ref->u.idref.disp, ref->u.idref.id, r);
+ default:
+ assert(0);
+ return E_FAIL;
+ }
+}
+
+static HRESULT exprval_call(script_ctx_t *ctx, exprval_t *ref, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
+{
+ switch(ref->type) {
+ case EXPRVAL_STACK_REF: {
+ jsval_t v = ctx->stack[ref->u.off];
+
+ if(!is_object_instance(v)) {
+ FIXME("invoke %s\n", debugstr_jsval(v));
+ return E_FAIL;
+ }
+
+ return disp_call_value(ctx, get_object(v), NULL, flags, argc, argv, r);
+ }
+ case EXPRVAL_IDREF:
+ return disp_call(ctx, ref->u.idref.disp, ref->u.idref.id, flags, argc, argv, r);
+ default:
+ assert(0);
+ return E_FAIL;
+ }
+}
+
+/* ECMA-262 3rd Edition 8.7.1 */
+/* Steals input reference. */
+static HRESULT exprval_to_value(script_ctx_t *ctx, exprval_t *ref, jsval_t *r)
+{
+ HRESULT hres;
+
+ if(ref->type == EXPRVAL_JSVAL) {
+ *r = ref->u.val;
+ return S_OK;
+ }
+
+ hres = exprval_propget(ctx, ref, r);
+
+ if(ref->type == EXPRVAL_IDREF)
+ IDispatch_Release(ref->u.idref.disp);
+ return hres;
}
static void exprval_release(exprval_t *val)
if(val->u.idref.disp)
IDispatch_Release(val->u.idref.disp);
return;
+ case EXPRVAL_STACK_REF:
case EXPRVAL_INVALID:
return;
}
}
-/* ECMA-262 3rd Edition 8.7.1 */
-static HRESULT exprval_to_value(script_ctx_t *ctx, exprval_t *val, jsval_t *ret)
+static inline void exprval_set_exception(exprval_t *val, HRESULT hres)
{
- switch(val->type) {
- case EXPRVAL_JSVAL:
- *ret = val->u.val;
- val->u.val = jsval_undefined();
- return S_OK;
- case EXPRVAL_IDREF:
- if(!val->u.idref.disp) {
- FIXME("throw ReferenceError\n");
- return E_FAIL;
- }
-
- return disp_propget(ctx, val->u.idref.disp, val->u.idref.id, ret);
- case EXPRVAL_INVALID:
- assert(0);
- }
+ val->type = EXPRVAL_INVALID;
+ val->u.hres = hres;
+}
- ERR("type %d\n", val->type);
- return E_FAIL;
+static inline void exprval_set_disp_ref(exprval_t *ref, IDispatch *obj, DISPID id)
+{
+ ref->type = EXPRVAL_IDREF;
+#ifdef __REACTOS__ /* FIXME: Inspect */
+ IDispatch_AddRef(obj);
+ ref->u.idref.disp = obj;
+#else
+ IDispatch_AddRef(ref->u.idref.disp = obj);
+#endif
+ ref->u.idref.id = id;
}
-static void exprval_set_idref(exprval_t *val, IDispatch *disp, DISPID id)
+static inline jsval_t steal_ret(call_frame_t *frame)
{
- val->type = EXPRVAL_IDREF;
- val->u.idref.disp = disp;
- val->u.idref.id = id;
+ jsval_t r = frame->ret;
+ frame->ret = jsval_undefined();
+ return r;
+}
- if(disp)
- IDispatch_AddRef(disp);
+static inline void clear_ret(call_frame_t *frame)
+{
+ jsval_release(steal_ret(frame));
}
HRESULT scope_push(scope_chain_t *scope, jsdisp_t *jsobj, IDispatch *obj, scope_chain_t **ret)
IDispatch_AddRef(obj);
new_scope->jsobj = jsobj;
new_scope->obj = obj;
-
- if(scope) {
- scope_addref(scope);
- new_scope->next = scope;
- }else {
- new_scope->next = NULL;
- }
+ new_scope->frame = NULL;
+ new_scope->next = scope ? scope_addref(scope) : NULL;
*ret = new_scope;
return S_OK;
BSTR bstr;
HRESULT hres;
- jsdisp = iface_to_jsdisp((IUnknown*)disp);
+ jsdisp = iface_to_jsdisp(disp);
if(jsdisp) {
hres = jsdisp_get_id(jsdisp, name, flags, id);
jsdisp_release(jsdisp);
return S_OK;
}
+/*
+ * Transfers local variables from stack to variable object.
+ * It's slow, so we want to avoid it as much as possible.
+ */
+static HRESULT detach_variable_object(script_ctx_t *ctx, call_frame_t *frame, BOOL from_release)
+{
+ unsigned i;
+ HRESULT hres;
+
+ if(!frame->base_scope || !frame->base_scope->frame)
+ return S_OK;
+
+ TRACE("detaching %p\n", frame);
+
+ assert(frame == frame->base_scope->frame);
+ assert(frame->variable_obj == frame->base_scope->jsobj);
+
+ if(!from_release && !frame->arguments_obj) {
+ hres = setup_arguments_object(ctx, frame);
+ if(FAILED(hres))
+ return hres;
+ }
+
+ frame->base_scope->frame = NULL;
+
+ for(i = 0; i < frame->function->locals_cnt; i++) {
+ hres = jsdisp_propput_name(frame->variable_obj, frame->function->locals[i].name,
+ ctx->stack[local_off(frame, frame->function->locals[i].ref)]);
+ if(FAILED(hres))
+ return hres;
+ }
+
+ return S_OK;
+}
+
static BOOL lookup_global_members(script_ctx_t *ctx, BSTR identifier, exprval_t *ret)
{
named_item_t *item;
hres = disp_get_id(ctx, item->disp, identifier, identifier, 0, &id);
if(SUCCEEDED(hres)) {
if(ret)
- exprval_set_idref(ret, item->disp, id);
+ exprval_set_disp_ref(ret, item->disp, id);
return TRUE;
}
}
return FALSE;
}
+static int local_ref_cmp(const void *key, const void *ref)
+{
+ return strcmpW((const WCHAR*)key, ((const local_ref_t*)ref)->name);
+}
+
+local_ref_t *lookup_local(const function_code_t *function, const WCHAR *identifier)
+{
+ return bsearch(identifier, function->locals, function->locals_cnt, sizeof(*function->locals), local_ref_cmp);
+}
+
/* ECMA-262 3rd Edition 10.1.4 */
static HRESULT identifier_eval(script_ctx_t *ctx, BSTR identifier, exprval_t *ret)
{
if(ctx->call_ctx) {
for(scope = ctx->call_ctx->scope; scope; scope = scope->next) {
+ if(scope->frame) {
+ function_code_t *func = scope->frame->function;
+ local_ref_t *ref = lookup_local(func, identifier);
+ static const WCHAR argumentsW[] = {'a','r','g','u','m','e','n','t','s',0};
+
+ if(ref) {
+ ret->type = EXPRVAL_STACK_REF;
+ ret->u.off = local_off(scope->frame, ref->ref);
+ TRACE("returning ref %d for %d\n", ret->u.off, ref->ref);
+ return S_OK;
+ }
+
+ if(!strcmpW(identifier, argumentsW)) {
+ hres = detach_variable_object(ctx, scope->frame, FALSE);
+ if(FAILED(hres))
+ return hres;
+ }
+ }
if(scope->jsobj)
hres = jsdisp_get_id(scope->jsobj, identifier, fdexNameImplicit, &id);
else
hres = disp_get_id(ctx, scope->obj, identifier, identifier, fdexNameImplicit, &id);
if(SUCCEEDED(hres)) {
- exprval_set_idref(ret, scope->obj, id);
+ exprval_set_disp_ref(ret, scope->obj, id);
return S_OK;
}
}
hres = jsdisp_get_id(ctx->global, identifier, 0, &id);
if(SUCCEEDED(hres)) {
- exprval_set_idref(ret, to_disp(ctx->global), id);
+ exprval_set_disp_ref(ret, to_disp(ctx->global), id);
return S_OK;
}
if(lookup_global_members(ctx, identifier, ret))
return S_OK;
- ret->type = EXPRVAL_INVALID;
+ exprval_set_exception(ret, JS_E_UNDEFINED_VARIABLE);
return S_OK;
}
ctx->call_ctx->ip = dst;
}
-/* ECMA-262 3rd Edition 12.2 */
-static HRESULT interp_var_set(script_ctx_t *ctx)
-{
- const BSTR name = get_op_bstr(ctx, 0);
- jsval_t val;
- HRESULT hres;
-
- TRACE("%s\n", debugstr_w(name));
-
- val = stack_pop(ctx);
- hres = jsdisp_propput_name(ctx->call_ctx->variable_obj, name, val);
- jsval_release(val);
- return hres;
-}
-
/* ECMA-262 3rd Edition 12.6.4 */
static HRESULT interp_forin(script_ctx_t *ctx)
{
const HRESULT arg = get_op_uint(ctx, 0);
- IDispatch *var_obj, *obj = NULL;
+ IDispatch *obj = NULL;
IDispatchEx *dispex;
- DISPID id, var_id;
+ exprval_t prop_ref;
+ DISPID id;
BSTR name = NULL;
HRESULT hres;
assert(is_number(stack_top(ctx)));
id = get_number(stack_top(ctx));
- var_obj = stack_topn_objid(ctx, 1, &var_id);
- if(!var_obj) {
- FIXME("invalid ref\n");
+ if(!stack_topn_exprval(ctx, 1, &prop_ref)) {
+ FIXME("invalid ref: %08x\n", prop_ref.u.hres);
return E_FAIL;
}
stack_pop(ctx);
stack_push(ctx, jsval_number(id)); /* safe, just after pop() */
- hres = disp_propput(ctx, var_obj, var_id, jsval_string(str));
+ hres = exprval_propput(ctx, &prop_ref, jsval_string(str));
jsstr_release(str);
if(FAILED(hres))
return hres;
const WCHAR *name;
jsstr_t *name_str;
IDispatch *obj;
+ exprval_t ref;
DISPID id;
HRESULT hres;
hres = disp_get_id(ctx, obj, name, NULL, arg, &id);
jsstr_release(name_str);
- if(FAILED(hres)) {
+ if(SUCCEEDED(hres)) {
+ ref.type = EXPRVAL_IDREF;
+ ref.u.idref.disp = obj;
+ ref.u.idref.id = id;
+ }else {
IDispatch_Release(obj);
if(hres == DISP_E_UNKNOWNNAME && !(arg & fdexNameEnsure)) {
- obj = NULL;
- id = JS_E_INVALID_PROPERTY;
+ exprval_set_exception(&ref, JS_E_INVALID_PROPERTY);
+ hres = S_OK;
}else {
ERR("failed %08x\n", hres);
return hres;
}
}
- return stack_push_objid(ctx, obj, id);
+ return stack_push_exprval(ctx, &ref);
}
/* ECMA-262 3rd Edition 11.2.1 */
static HRESULT interp_refval(script_ctx_t *ctx)
{
- IDispatch *disp;
+ exprval_t ref;
jsval_t v;
- DISPID id;
HRESULT hres;
TRACE("\n");
- disp = stack_topn_objid(ctx, 0, &id);
- if(!disp)
+ if(!stack_topn_exprval(ctx, 0, &ref))
return throw_reference_error(ctx, JS_E_ILLEGAL_ASSIGN, NULL);
- hres = disp_propget(ctx, disp, id, &v);
+ hres = exprval_propget(ctx, &ref, &v);
if(FAILED(hres))
return hres;
const unsigned argn = get_op_uint(ctx, 0);
const int do_ret = get_op_int(ctx, 1);
call_frame_t *frame = ctx->call_ctx;
- IDispatch *obj;
- DISPID id;
+ exprval_t ref;
TRACE("%d %d\n", argn, do_ret);
- obj = stack_topn_objid(ctx, argn, &id);
- if(!obj)
- return throw_type_error(ctx, id, NULL);
+ if(!stack_topn_exprval(ctx, argn, &ref))
+ return throw_type_error(ctx, ref.u.hres, NULL);
clear_ret(frame);
- return disp_call(ctx, obj, id, DISPATCH_METHOD | DISPATCH_JSCRIPT_CALLEREXECSSOURCE,
+ return exprval_call(ctx, &ref, DISPATCH_METHOD | DISPATCH_JSCRIPT_CALLEREXECSSOURCE,
argn, stack_args(ctx, argn), do_ret ? &frame->ret : NULL);
}
return stack_push(ctx, jsval_disp(frame->this_obj));
}
-/* ECMA-262 3rd Edition 10.1.4 */
-static HRESULT interp_ident(script_ctx_t *ctx)
+static HRESULT interp_identifier_ref(script_ctx_t *ctx, BSTR identifier, unsigned flags)
{
- const BSTR arg = get_op_bstr(ctx, 0);
exprval_t exprval;
- jsval_t v;
HRESULT hres;
- TRACE("%s\n", debugstr_w(arg));
+ hres = identifier_eval(ctx, identifier, &exprval);
+ if(FAILED(hres))
+ return hres;
- hres = identifier_eval(ctx, arg, &exprval);
+ if(exprval.type == EXPRVAL_INVALID && (flags & fdexNameEnsure)) {
+ DISPID id;
+
+ hres = jsdisp_get_id(ctx->global, identifier, fdexNameEnsure, &id);
+ if(FAILED(hres))
+ return hres;
+
+ exprval_set_disp_ref(&exprval, to_disp(ctx->global), id);
+ }
+
+ if(exprval.type == EXPRVAL_JSVAL || exprval.type == EXPRVAL_INVALID) {
+ WARN("invalid ref\n");
+ exprval_release(&exprval);
+ exprval_set_exception(&exprval, JS_E_OBJECT_EXPECTED);
+ }
+
+ return stack_push_exprval(ctx, &exprval);
+}
+
+static HRESULT identifier_value(script_ctx_t *ctx, BSTR identifier)
+{
+ exprval_t exprval;
+ jsval_t v;
+ HRESULT hres;
+
+ hres = identifier_eval(ctx, identifier, &exprval);
if(FAILED(hres))
return hres;
if(exprval.type == EXPRVAL_INVALID)
- return throw_type_error(ctx, JS_E_UNDEFINED_VARIABLE, arg);
+ return throw_type_error(ctx, exprval.u.hres, identifier);
hres = exprval_to_value(ctx, &exprval, &v);
- exprval_release(&exprval);
if(FAILED(hres))
return hres;
return stack_push(ctx, v);
}
-/* ECMA-262 3rd Edition 10.1.4 */
-static HRESULT interp_identid(script_ctx_t *ctx)
+static HRESULT interp_local_ref(script_ctx_t *ctx)
{
- const BSTR arg = get_op_bstr(ctx, 0);
+ const int arg = get_op_int(ctx, 0);
const unsigned flags = get_op_uint(ctx, 1);
- exprval_t exprval;
+ call_frame_t *frame = ctx->call_ctx;
+ exprval_t ref;
+
+ TRACE("%d\n", arg);
+
+ if(!frame->base_scope || !frame->base_scope->frame)
+ return interp_identifier_ref(ctx, local_name(frame, arg), flags);
+
+ ref.type = EXPRVAL_STACK_REF;
+ ref.u.off = local_off(frame, arg);
+ return stack_push_exprval(ctx, &ref);
+}
+
+static HRESULT interp_local(script_ctx_t *ctx)
+{
+ const int arg = get_op_int(ctx, 0);
+ call_frame_t *frame = ctx->call_ctx;
+ jsval_t copy;
HRESULT hres;
- TRACE("%s %x\n", debugstr_w(arg), flags);
+ TRACE("%d\n", arg);
- hres = identifier_eval(ctx, arg, &exprval);
+ if(!frame->base_scope || !frame->base_scope->frame)
+ return identifier_value(ctx, local_name(frame, arg));
+
+ hres = jsval_copy(ctx->stack[local_off(frame, arg)], ©);
if(FAILED(hres))
return hres;
- if(exprval.type == EXPRVAL_INVALID && (flags & fdexNameEnsure)) {
- DISPID id;
+ return stack_push(ctx, copy);
+}
- hres = jsdisp_get_id(ctx->global, arg, fdexNameEnsure, &id);
- if(FAILED(hres))
- return hres;
+/* ECMA-262 3rd Edition 10.1.4 */
+static HRESULT interp_ident(script_ctx_t *ctx)
+{
+ const BSTR arg = get_op_bstr(ctx, 0);
- exprval_set_idref(&exprval, to_disp(ctx->global), id);
- }
+ TRACE("%s\n", debugstr_w(arg));
- if(exprval.type != EXPRVAL_IDREF) {
- WARN("invalid ref\n");
- exprval_release(&exprval);
- return stack_push_objid(ctx, NULL, JS_E_OBJECT_EXPECTED);
- }
+ return identifier_value(ctx, arg);
+}
- return stack_push_objid(ctx, exprval.u.idref.disp, exprval.u.idref.id);
+/* ECMA-262 3rd Edition 10.1.4 */
+static HRESULT interp_identid(script_ctx_t *ctx)
+{
+ const BSTR arg = get_op_bstr(ctx, 0);
+ const unsigned flags = get_op_uint(ctx, 1);
+
+ TRACE("%s %x\n", debugstr_w(arg), flags);
+
+ return interp_identifier_ref(ctx, arg, flags);
}
/* ECMA-262 3rd Edition 7.8.1 */
return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
}
- obj = iface_to_jsdisp((IUnknown*)get_object(v));
+ obj = iface_to_jsdisp(get_object(v));
IDispatch_Release(get_object(v));
if(!obj) {
FIXME("non-jsdisp objects not supported\n");
if(is_object_instance(prot)) {
if(is_object_instance(v))
- tmp = iface_to_jsdisp((IUnknown*)get_object(v));
+ tmp = iface_to_jsdisp(get_object(v));
for(iter = tmp; !ret && iter; iter = iter->prototype) {
hres = disp_cmp(get_object(prot), to_disp(iter), &ret);
if(FAILED(hres))
return hres;
switch(exprval.type) {
+ case EXPRVAL_STACK_REF:
+ ret = FALSE;
+ break;
case EXPRVAL_IDREF:
hres = disp_delete(exprval.u.idref.disp, exprval.u.idref.id, &ret);
IDispatch_Release(exprval.u.idref.disp);
case JSV_OBJECT: {
jsdisp_t *dispex;
- if(get_object(v) && (dispex = iface_to_jsdisp((IUnknown*)get_object(v)))) {
+ if(get_object(v) && (dispex = iface_to_jsdisp(get_object(v)))) {
*ret = is_class(dispex, JSCLASS_FUNCTION) ? functionW : objectW;
jsdisp_release(dispex);
}else {
static HRESULT interp_typeofid(script_ctx_t *ctx)
{
const WCHAR *ret;
- IDispatch *obj;
+ exprval_t ref;
jsval_t v;
- DISPID id;
HRESULT hres;
TRACE("\n");
- obj = stack_pop_objid(ctx, &id);
- if(!obj)
+ if(!stack_pop_exprval(ctx, &ref))
return stack_push(ctx, jsval_string(jsstr_undefined()));
- hres = disp_propget(ctx, obj, id, &v);
- IDispatch_Release(obj);
+ hres = exprval_propget(ctx, &ref, &v);
+ exprval_release(&ref);
if(FAILED(hres))
return stack_push_string(ctx, unknownW);
if(FAILED(hres))
return hres;
- if(exprval.type == EXPRVAL_INVALID) {
- hres = stack_push(ctx, jsval_string(jsstr_undefined()));
- exprval_release(&exprval);
- return hres;
- }
+ if(exprval.type == EXPRVAL_INVALID)
+ return stack_push(ctx, jsval_string(jsstr_undefined()));
hres = exprval_to_value(ctx, &exprval, &v);
- exprval_release(&exprval);
if(FAILED(hres))
return hres;
static HRESULT interp_postinc(script_ctx_t *ctx)
{
const int arg = get_op_int(ctx, 0);
- IDispatch *obj;
- DISPID id;
+ exprval_t ref;
jsval_t v;
HRESULT hres;
TRACE("%d\n", arg);
- obj = stack_pop_objid(ctx, &id);
- if(!obj)
+ if(!stack_pop_exprval(ctx, &ref))
return throw_type_error(ctx, JS_E_OBJECT_EXPECTED, NULL);
- hres = disp_propget(ctx, obj, id, &v);
+ hres = exprval_propget(ctx, &ref, &v);
if(SUCCEEDED(hres)) {
double n;
hres = to_number(ctx, v, &n);
if(SUCCEEDED(hres))
- hres = disp_propput(ctx, obj, id, jsval_number(n+(double)arg));
+ hres = exprval_propput(ctx, &ref, jsval_number(n+(double)arg));
if(FAILED(hres))
jsval_release(v);
}
- IDispatch_Release(obj);
+ exprval_release(&ref);
if(FAILED(hres))
return hres;
static HRESULT interp_preinc(script_ctx_t *ctx)
{
const int arg = get_op_int(ctx, 0);
- IDispatch *obj;
+ exprval_t ref;
double ret;
- DISPID id;
jsval_t v;
HRESULT hres;
TRACE("%d\n", arg);
- obj = stack_pop_objid(ctx, &id);
- if(!obj)
+ if(!stack_pop_exprval(ctx, &ref))
return throw_type_error(ctx, JS_E_OBJECT_EXPECTED, NULL);
- hres = disp_propget(ctx, obj, id, &v);
+ hres = exprval_propget(ctx, &ref, &v);
if(SUCCEEDED(hres)) {
double n;
jsval_release(v);
if(SUCCEEDED(hres)) {
ret = n+(double)arg;
- hres = disp_propput(ctx, obj, id, jsval_number(ret));
+ hres = exprval_propput(ctx, &ref, jsval_number(ret));
}
}
- IDispatch_Release(obj);
+ exprval_release(&ref);
if(FAILED(hres))
return hres;
/* ECMA-262 3rd Edition 11.13.1 */
static HRESULT interp_assign(script_ctx_t *ctx)
{
- IDispatch *disp;
- DISPID id;
+ exprval_t ref;
jsval_t v;
HRESULT hres;
v = stack_pop(ctx);
- disp = stack_pop_objid(ctx, &id);
- if(!disp) {
+ if(!stack_pop_exprval(ctx, &ref)) {
jsval_release(v);
return throw_reference_error(ctx, JS_E_ILLEGAL_ASSIGN, NULL);
}
- hres = disp_propput(ctx, disp, id, v);
- IDispatch_Release(disp);
+ hres = exprval_propput(ctx, &ref, v);
+ exprval_release(&ref);
if(FAILED(hres)) {
jsval_release(v);
return hres;
static HRESULT interp_assign_call(script_ctx_t *ctx)
{
const unsigned argc = get_op_uint(ctx, 0);
- IDispatch *disp;
+ exprval_t ref;
jsval_t v;
- DISPID id;
HRESULT hres;
TRACE("%u\n", argc);
- disp = stack_topn_objid(ctx, argc+1, &id);
- if(!disp)
+ if(!stack_topn_exprval(ctx, argc+1, &ref))
return throw_reference_error(ctx, JS_E_ILLEGAL_ASSIGN, NULL);
- hres = disp_call(ctx, disp, id, DISPATCH_PROPERTYPUT, argc+1, stack_args(ctx, argc+1), NULL);
+ hres = exprval_call(ctx, &ref, DISPATCH_PROPERTYPUT, argc+1, stack_args(ctx, argc+1), NULL);
if(FAILED(hres))
return hres;
#undef X
};
-static void release_call_frame(call_frame_t *frame)
+static void pop_call_frame(script_ctx_t *ctx)
{
- if(frame->arguments_obj) {
- /* Reset arguments value to cut the reference cycle. Note that since all activation contexts have
- * their own arguments property, it's impossible to use prototype's one during name lookup */
- static const WCHAR argumentsW[] = {'a','r','g','u','m','e','n','t','s',0};
- jsdisp_propput_name(frame->variable_obj, argumentsW, jsval_undefined());
- jsdisp_release(frame->arguments_obj);
+ call_frame_t *frame = ctx->call_ctx;
+
+ frame->stack_base -= frame->pop_locals + frame->pop_variables;
+
+ assert(frame->scope == frame->base_scope);
+
+ /* If current scope will be kept alive, we need to transfer local variables to its variable object. */
+ if(frame->scope && frame->scope->ref > 1) {
+ HRESULT hres = detach_variable_object(ctx, frame, TRUE);
+ if(FAILED(hres))
+ ERR("Failed to detach variable object: %08x\n", hres);
}
+
+ if(frame->arguments_obj)
+ detach_arguments_object(frame->arguments_obj);
+ if(frame->scope)
+ scope_release(frame->scope);
+
+ if(frame->pop_variables)
+ stack_popn(ctx, frame->pop_variables);
+ stack_popn(ctx, frame->pop_locals);
+
+ ctx->call_ctx = frame->prev_frame;
+
if(frame->function_instance)
jsdisp_release(frame->function_instance);
if(frame->variable_obj)
jsdisp_release(frame->variable_obj);
if(frame->this_obj)
IDispatch_Release(frame->this_obj);
- if(frame->scope)
- scope_release(frame->scope);
jsval_release(frame->ret);
release_bytecode(frame->bytecode);
heap_free(frame);
stack_popn(ctx, ctx->stack_top-frame->stack_base);
- ctx->call_ctx = frame->prev_frame;
flags = frame->flags;
- release_call_frame(frame);
+ pop_call_frame(ctx);
if(!(flags & EXEC_RETURN_TO_INTERP))
return exception_hres;
}
assert(ctx->stack_top == frame->stack_base);
assert(frame->scope == frame->base_scope);
- ctx->call_ctx = frame->prev_frame;
if(return_to_interp) {
- clear_ret(ctx->call_ctx);
- ctx->call_ctx->ret = steal_ret(frame);
+ clear_ret(frame->prev_frame);
+ frame->prev_frame->ret = steal_ret(frame);
}else if(r) {
*r = steal_ret(frame);
}
- release_call_frame(frame);
+ pop_call_frame(ctx);
if(!return_to_interp)
break;
}else {
return hres;
hres = exprval_to_value(ctx, &exprval, &v);
- exprval_release(&exprval);
if(FAILED(hres))
return hres;
return hres;
}
+static HRESULT setup_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_t *scope_chain, jsdisp_t *variable_object, unsigned argc, jsval_t *argv)
+{
+ const unsigned orig_stack = ctx->stack_top;
+ scope_chain_t *scope;
+ unsigned i;
+ jsval_t v;
+ HRESULT hres;
+
+ /* If arguments are already on the stack, we may use them. */
+ if(argv + argc == ctx->stack + ctx->stack_top) {
+ frame->arguments_off = argv - ctx->stack;
+ i = argc;
+ }else {
+ frame->arguments_off = ctx->stack_top;
+ for(i = 0; i < argc; i++) {
+ hres = jsval_copy(argv[i], &v);
+ if(SUCCEEDED(hres))
+ hres = stack_push(ctx, v);
+ if(FAILED(hres)) {
+ stack_popn(ctx, i);
+ return hres;
+ }
+ }
+ }
+
+ /* If fewer than declared arguments were passed, fill remaining with undefined value. */
+ for(; i < frame->function->param_cnt; i++) {
+ hres = stack_push(ctx, jsval_undefined());
+ if(FAILED(hres)) {
+ stack_popn(ctx, ctx->stack_top - orig_stack);
+ return hres;
+ }
+ }
+
+ frame->pop_locals = ctx->stack_top - orig_stack;
+
+ frame->variables_off = ctx->stack_top;
+
+ for(i = 0; i < frame->function->var_cnt; i++) {
+ hres = stack_push(ctx, jsval_undefined());
+ if(FAILED(hres)) {
+ stack_popn(ctx, ctx->stack_top - orig_stack);
+ return hres;
+ }
+ }
+
+ frame->pop_variables = i;
+
+ hres = scope_push(scope_chain, variable_object, to_disp(variable_object), &scope);
+ if(FAILED(hres)) {
+ stack_popn(ctx, ctx->stack_top - orig_stack);
+ return hres;
+ }
+
+ for(i = 0; i < frame->function->func_cnt; i++) {
+ if(frame->function->funcs[i].name && !frame->function->funcs[i].event_target) {
+ jsdisp_t *func_obj;
+ unsigned off;
+
+ hres = create_source_function(ctx, frame->bytecode, frame->function->funcs+i, scope, &func_obj);
+ if(FAILED(hres)) {
+ stack_popn(ctx, ctx->stack_top - orig_stack);
+ scope_release(scope);
+ return hres;
+ }
+
+ off = local_off(frame, frame->function->funcs[i].local_ref);
+ jsval_release(ctx->stack[off]);
+ ctx->stack[off] = jsval_obj(func_obj);
+ }
+ }
+
+ scope->frame = frame;
+ frame->base_scope = frame->scope = scope;
+ return S_OK;
+}
+
HRESULT exec_source(script_ctx_t *ctx, DWORD flags, bytecode_t *bytecode, function_code_t *function, scope_chain_t *scope,
- IDispatch *this_obj, jsdisp_t *function_instance, jsdisp_t *variable_obj, jsdisp_t *arguments_obj, jsval_t *r)
+ IDispatch *this_obj, jsdisp_t *function_instance, jsdisp_t *variable_obj, unsigned argc, jsval_t *argv, jsval_t *r)
{
call_frame_t *frame;
unsigned i;
for(i = 0; i < function->func_cnt; i++) {
jsdisp_t *func_obj;
- if(!function->funcs[i].name)
+ if(!function->funcs[i].event_target)
continue;
hres = create_source_function(ctx, bytecode, function->funcs+i, scope, &func_obj);
if(FAILED(hres))
return hres;
- if(function->funcs[i].event_target)
- hres = bind_event_target(ctx, function->funcs+i, func_obj);
- else
- hres = jsdisp_propput_name(variable_obj, function->funcs[i].name, jsval_obj(func_obj));
+ hres = bind_event_target(ctx, function->funcs+i, func_obj);
jsdisp_release(func_obj);
if(FAILED(hres))
return hres;
}
- for(i=0; i < function->var_cnt; i++) {
- if(!(flags & EXEC_GLOBAL) || !lookup_global_members(ctx, function->variables[i], NULL)) {
- DISPID id = 0;
+ if(flags & (EXEC_GLOBAL | EXEC_EVAL)) {
+ for(i=0; i < function->var_cnt; i++) {
+ TRACE("[%d] %s %d\n", i, debugstr_w(function->variables[i].name), function->variables[i].func_id);
+ if(function->variables[i].func_id != -1) {
+ jsdisp_t *func_obj;
- hres = jsdisp_get_id(variable_obj, function->variables[i], fdexNameEnsure, &id);
- if(FAILED(hres))
- return hres;
+ hres = create_source_function(ctx, bytecode, function->funcs+function->variables[i].func_id, scope, &func_obj);
+ if(FAILED(hres))
+ return hres;
+
+ hres = jsdisp_propput_name(variable_obj, function->variables[i].name, jsval_obj(func_obj));
+ jsdisp_release(func_obj);
+ }else if(!(flags & EXEC_GLOBAL) || !lookup_global_members(ctx, function->variables[i].name, NULL)) {
+ DISPID id = 0;
+
+ hres = jsdisp_get_id(variable_obj, function->variables[i].name, fdexNameEnsure, &id);
+ if(FAILED(hres))
+ return hres;
+ }
}
}
if(this_obj) {
jsdisp_t *jsthis;
- jsthis = iface_to_jsdisp((IUnknown*)this_obj);
+ jsthis = iface_to_jsdisp(this_obj);
if(jsthis) {
if(jsthis->builtin_info->class == JSCLASS_GLOBAL || jsthis->builtin_info->class == JSCLASS_NONE)
this_obj = NULL;
}
}
+ if(ctx->call_ctx && (flags & EXEC_EVAL)) {
+ hres = detach_variable_object(ctx, ctx->call_ctx, FALSE);
+ if(FAILED(hres))
+ return hres;
+ }
+
frame = heap_alloc_zero(sizeof(*frame));
if(!frame)
return E_OUTOFMEMORY;
- frame->bytecode = bytecode_addref(bytecode);
frame->function = function;
- frame->ip = function->instr_off;
- frame->stack_base = ctx->stack_top;
frame->ret = jsval_undefined();
- if(scope)
+ frame->argc = argc;
+ frame->bytecode = bytecode_addref(bytecode);
+
+ if(!(flags & (EXEC_GLOBAL|EXEC_EVAL))) {
+ hres = setup_scope(ctx, frame, scope, variable_obj, argc, argv);
+ if(FAILED(hres)) {
+ release_bytecode(frame->bytecode);
+ heap_free(frame);
+ return hres;
+ }
+ }else if(scope) {
frame->base_scope = frame->scope = scope_addref(scope);
+ }
+ frame->ip = function->instr_off;
+ frame->stack_base = ctx->stack_top;
if(this_obj)
frame->this_obj = this_obj;
else if(ctx->host_global)
if(function_instance)
frame->function_instance = jsdisp_addref(function_instance);
- if(arguments_obj)
- frame->arguments_obj = jsdisp_addref(arguments_obj);
frame->flags = flags;
frame->variable_obj = jsdisp_addref(variable_obj);
X(int, 1, ARG_INT, 0) \
X(jmp, 0, ARG_ADDR, 0) \
X(jmp_z, 0, ARG_ADDR, 0) \
+ X(local, 1, ARG_INT, 0) \
+ X(local_ref, 1, ARG_INT, ARG_UINT) \
X(lshift, 1, 0,0) \
X(lt, 1, 0,0) \
X(lteq, 1, 0,0) \
X(setret, 1, 0,0) \
X(sub, 1, 0,0) \
X(undefined, 1, 0,0) \
- X(var_set, 1, ARG_BSTR, 0) \
X(void, 1, 0,0) \
X(xor, 1, 0,0)
} u;
} instr_t;
+typedef struct {
+ BSTR name;
+ int ref;
+} local_ref_t;
+
typedef struct _function_code_t {
BSTR name;
+ int local_ref;
BSTR event_target;
unsigned instr_off;
struct _function_code_t *funcs;
unsigned var_cnt;
- BSTR *variables;
+ struct {
+ BSTR name;
+ int func_id; /* -1 if not a function */
+ } *variables;
unsigned param_cnt;
BSTR *params;
+
+ unsigned locals_cnt;
+ local_ref_t *locals;
} function_code_t;
+local_ref_t *lookup_local(const function_code_t*,const WCHAR*) DECLSPEC_HIDDEN;
+
typedef struct _bytecode_t {
LONG ref;
LONG ref;
jsdisp_t *jsobj;
IDispatch *obj;
+ struct _call_frame_t *frame;
struct _scope_chain_t *next;
} scope_chain_t;
jsdisp_t *arguments_obj;
DWORD flags;
+ unsigned argc;
+ unsigned pop_locals;
+ unsigned arguments_off;
+ unsigned variables_off;
+ unsigned pop_variables;
+
bytecode_t *bytecode;
function_code_t *function;
#define EXEC_GLOBAL 0x0001
#define EXEC_CONSTRUCTOR 0x0002
#define EXEC_RETURN_TO_INTERP 0x0004
+#define EXEC_EVAL 0x0008
HRESULT exec_source(script_ctx_t*,DWORD,bytecode_t*,function_code_t*,scope_chain_t*,IDispatch*,
- jsdisp_t*,jsdisp_t*,jsdisp_t*,jsval_t*) DECLSPEC_HIDDEN;
+ jsdisp_t*,jsdisp_t*,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN;
HRESULT create_source_function(script_ctx_t*,bytecode_t*,function_code_t*,scope_chain_t*,jsdisp_t**) DECLSPEC_HIDDEN;
+HRESULT setup_arguments_object(script_ctx_t*,call_frame_t*) DECLSPEC_HIDDEN;
+void detach_arguments_object(jsdisp_t*) DECLSPEC_HIDDEN;
typedef struct {
jsdisp_t jsdisp;
FunctionInstance *function;
- jsdisp_t *var_obj;
+ jsval_t *buf;
+ call_frame_t *frame;
+ unsigned argc;
} ArgumentsInstance;
static inline FunctionInstance *function_from_jsdisp(jsdisp_t *jsdisp)
return is_vclass(jsthis, JSCLASS_FUNCTION) ? function_from_vdisp(jsthis) : NULL;
}
+static inline ArgumentsInstance *arguments_from_jsdisp(jsdisp_t *jsdisp)
+{
+ return CONTAINING_RECORD(jsdisp, ArgumentsInstance, jsdisp);
+}
+
static const WCHAR prototypeW[] = {'p','r','o','t','o','t', 'y', 'p','e',0};
static const WCHAR lengthW[] = {'l','e','n','g','t','h',0};
static const WCHAR callW[] = {'c','a','l','l',0};
static const WCHAR argumentsW[] = {'a','r','g','u','m','e','n','t','s',0};
-static HRESULT init_parameters(jsdisp_t *var_disp, FunctionInstance *function, unsigned argc, jsval_t *argv)
-{
- DWORD i=0;
- HRESULT hres;
-
- for(i=0; i < function->func_code->param_cnt; i++) {
- hres = jsdisp_propput_name(var_disp, function->func_code->params[i],
- i < argc ? argv[i] : jsval_undefined());
- if(FAILED(hres))
- return hres;
- }
-
- return S_OK;
-}
-
static HRESULT Arguments_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
jsval_t *r)
{
{
ArgumentsInstance *arguments = (ArgumentsInstance*)jsdisp;
+ TRACE("(%p)\n", arguments);
+
+ if(arguments->buf) {
+ unsigned i;
+ for(i = 0; i < arguments->argc; i++)
+ jsval_release(arguments->buf[i]);
+ heap_free(arguments->buf);
+ }
+
jsdisp_release(&arguments->function->dispex);
- jsdisp_release(arguments->var_obj);
heap_free(arguments);
}
static unsigned Arguments_idx_length(jsdisp_t *jsdisp)
{
ArgumentsInstance *arguments = (ArgumentsInstance*)jsdisp;
- return arguments->function->length;
+ return arguments->argc;
+}
+
+static jsval_t *get_argument_ref(ArgumentsInstance *arguments, unsigned idx)
+{
+ if(arguments->buf)
+ return arguments->buf + idx;
+ if(arguments->frame->base_scope->frame || idx >= arguments->frame->function->param_cnt)
+ return arguments->jsdisp.ctx->stack + arguments->frame->arguments_off + idx;
+ return NULL;
}
-static HRESULT Arguments_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *res)
+static HRESULT Arguments_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r)
{
ArgumentsInstance *arguments = (ArgumentsInstance*)jsdisp;
+ jsval_t *ref;
TRACE("%p[%u]\n", arguments, idx);
+ if((ref = get_argument_ref(arguments, idx)))
+ return jsval_copy(*ref, r);
+
/* FIXME: Accessing by name won't work for duplicated argument names */
- return jsdisp_propget_name(arguments->var_obj, arguments->function->func_code->params[idx], res);
+ return jsdisp_propget_name(arguments->frame->base_scope->jsobj, arguments->function->func_code->params[idx], r);
}
static HRESULT Arguments_idx_put(jsdisp_t *jsdisp, unsigned idx, jsval_t val)
{
ArgumentsInstance *arguments = (ArgumentsInstance*)jsdisp;
+ jsval_t *ref;
+ HRESULT hres;
TRACE("%p[%u] = %s\n", arguments, idx, debugstr_jsval(val));
+ if((ref = get_argument_ref(arguments, idx))) {
+ jsval_t copy;
+ hres = jsval_copy(val, ©);
+ if(FAILED(hres))
+ return hres;
+
+ jsval_release(*ref);
+ *ref = copy;
+ return S_OK;
+ }
+
/* FIXME: Accessing by name won't work for duplicated argument names */
- return jsdisp_propput_name(arguments->var_obj, arguments->function->func_code->params[idx], val);
+ return jsdisp_propput_name(arguments->frame->base_scope->jsobj, arguments->function->func_code->params[idx], val);
}
static const builtin_info_t Arguments_info = {
Arguments_idx_put
};
-static HRESULT create_arguments(script_ctx_t *ctx, FunctionInstance *calee, jsdisp_t *var_obj,
- unsigned argc, jsval_t *argv, jsdisp_t **ret)
+HRESULT setup_arguments_object(script_ctx_t *ctx, call_frame_t *frame)
{
ArgumentsInstance *args;
- unsigned i;
HRESULT hres;
static const WCHAR caleeW[] = {'c','a','l','l','e','e',0};
return hres;
}
- jsdisp_addref(&calee->dispex);
- args->function = calee;
- args->var_obj = jsdisp_addref(var_obj);
+ args->function = function_from_jsdisp(jsdisp_addref(frame->function_instance));
+ args->argc = frame->argc;
+ args->frame = frame;
- /* Store unnamed arguments directly in arguments object */
- for(i = calee->length; i < argc; i++) {
- WCHAR buf[12];
-
- static const WCHAR formatW[] = {'%','d',0};
-
- sprintfW(buf, formatW, i);
- hres = jsdisp_propput_dontenum(&args->jsdisp, buf, argv[i]);
- if(FAILED(hres))
- break;
- }
-
- if(SUCCEEDED(hres)) {
- hres = jsdisp_propput_dontenum(&args->jsdisp, lengthW, jsval_number(argc));
- if(SUCCEEDED(hres))
- hres = jsdisp_propput_dontenum(&args->jsdisp, caleeW, jsval_disp(to_disp(&calee->dispex)));
- }
+ hres = jsdisp_propput_dontenum(&args->jsdisp, lengthW, jsval_number(args->argc));
+ if(SUCCEEDED(hres))
+ hres = jsdisp_propput_dontenum(&args->jsdisp, caleeW, jsval_disp(to_disp(&args->function->dispex)));
+ if(SUCCEEDED(hres))
+ hres = jsdisp_propput(frame->base_scope->jsobj, argumentsW, PROPF_DONTDELETE, jsval_obj(&args->jsdisp));
if(FAILED(hres)) {
jsdisp_release(&args->jsdisp);
return hres;
}
- *ret = &args->jsdisp;
+ frame->arguments_obj = &args->jsdisp;
return S_OK;
}
-static HRESULT create_var_disp(script_ctx_t *ctx, FunctionInstance *function, unsigned argc, jsval_t *argv, jsdisp_t **ret)
+void detach_arguments_object(jsdisp_t *args_disp)
{
- jsdisp_t *var_disp;
+ ArgumentsInstance *arguments = arguments_from_jsdisp(args_disp);
+ call_frame_t *frame = arguments->frame;
+ const BOOL on_stack = frame->base_scope->frame == frame;
HRESULT hres;
- hres = create_dispex(ctx, NULL, NULL, &var_disp);
- if(FAILED(hres))
- return hres;
-
- hres = init_parameters(var_disp, function, argc, argv);
- if(FAILED(hres)) {
- jsdisp_release(var_disp);
- return hres;
+ /* Reset arguments value to cut the reference cycle. Note that since all activation contexts have
+ * their own arguments property, it's impossible to use prototype's one during name lookup */
+ jsdisp_propput_name(frame->base_scope->jsobj, argumentsW, jsval_undefined());
+ arguments->frame = NULL;
+
+ /* Don't bother coppying arguments if call frame holds the last reference. */
+ if(arguments->jsdisp.ref > 1) {
+ arguments->buf = heap_alloc(arguments->argc * sizeof(*arguments->buf));
+ if(arguments->buf) {
+ int i;
+
+ for(i = 0; i < arguments->argc ; i++) {
+ if(on_stack || i >= frame->function->param_cnt)
+ hres = jsval_copy(arguments->jsdisp.ctx->stack[frame->arguments_off + i], arguments->buf+i);
+ else
+ hres = jsdisp_propget_name(frame->base_scope->jsobj, frame->function->params[i], arguments->buf+i);
+ if(FAILED(hres))
+ arguments->buf[i] = jsval_undefined();
+ }
+ }else {
+ ERR("out of memory\n");
+ arguments->argc = 0;
+ }
}
- *ret = var_disp;
- return S_OK;
+ jsdisp_release(frame->arguments_obj);
}
static HRESULT invoke_source(script_ctx_t *ctx, FunctionInstance *function, IDispatch *this_obj, unsigned argc, jsval_t *argv,
BOOL is_constructor, BOOL caller_execs_source, jsval_t *r)
{
- jsdisp_t *var_disp, *arg_disp;
- scope_chain_t *scope;
+ jsdisp_t *var_disp;
+ DWORD exec_flags = 0;
HRESULT hres;
if(ctx->state == SCRIPTSTATE_UNINITIALIZED || ctx->state == SCRIPTSTATE_CLOSED) {
return E_FAIL;
}
- hres = create_var_disp(ctx, function, argc, argv, &var_disp);
+ hres = create_dispex(ctx, NULL, NULL, &var_disp);
if(FAILED(hres))
return hres;
- hres = create_arguments(ctx, function, var_disp, argc, argv, &arg_disp);
- if(FAILED(hres)) {
- jsdisp_release(var_disp);
- return hres;
- }
+ if(caller_execs_source)
+ exec_flags |= EXEC_RETURN_TO_INTERP;
+ if(is_constructor)
+ exec_flags |= EXEC_CONSTRUCTOR;
+ hres = exec_source(ctx, exec_flags, function->code, function->func_code, function->scope_chain, this_obj,
+ &function->dispex, var_disp, argc, argv, r);
- hres = jsdisp_propput(var_disp, argumentsW, PROPF_DONTDELETE, jsval_obj(arg_disp));
- if(FAILED(hres)) {
- jsdisp_release(arg_disp);
- jsdisp_release(var_disp);
- return hres;
- }
-
- hres = scope_push(function->scope_chain, var_disp, to_disp(var_disp), &scope);
- if(SUCCEEDED(hres)) {
- DWORD exec_flags = 0;
-
- if(caller_execs_source)
- exec_flags |= EXEC_RETURN_TO_INTERP;
- if(is_constructor)
- exec_flags |= EXEC_CONSTRUCTOR;
- hres = exec_source(ctx, exec_flags, function->code, function->func_code, scope, this_obj,
- &function->dispex, var_disp, arg_disp, r);
-
- scope_release(scope);
- }
-
- jsdisp_release(arg_disp);
jsdisp_release(var_disp);
return hres;
}
TRACE("\n");
- if(!(function = function_this(jsthis)))
+ if(!(function = function_this(jsthis)) && (jsthis->flags & VDISP_JSDISP))
return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
if(argc) {
jsdisp_t *arg_array = NULL;
if(is_object_instance(argv[1])) {
- arg_array = iface_to_jsdisp((IUnknown*)get_object(argv[1]));
+ arg_array = iface_to_jsdisp(get_object(argv[1]));
if(arg_array &&
(!is_class(arg_array, JSCLASS_ARRAY) && !is_class(arg_array, JSCLASS_ARGUMENTS) )) {
jsdisp_release(arg_array);
}
}
- if(SUCCEEDED(hres))
- hres = call_function(ctx, function, this_obj, cnt, args, (flags & DISPATCH_JSCRIPT_CALLEREXECSSOURCE) != 0, r);
+ if(SUCCEEDED(hres)) {
+ if(function) {
+ hres = call_function(ctx, function, this_obj, cnt, args, (flags & DISPATCH_JSCRIPT_CALLEREXECSSOURCE) != 0, r);
+ }else {
+ jsval_t res;
+ hres = disp_call_value(ctx, jsthis->u.disp, this_obj, DISPATCH_METHOD, cnt, args, &res);
+ if(SUCCEEDED(hres)) {
+ if(r)
+ *r = res;
+ else
+ jsval_release(res);
+ }
+ }
+ }
if(this_obj)
IDispatch_Release(this_obj);
{
FunctionInstance *function = function_from_jsdisp(jsthis);
call_frame_t *frame;
+ HRESULT hres;
TRACE("\n");
for(frame = ctx->call_ctx; frame; frame = frame->prev_frame) {
if(frame->function_instance == &function->dispex) {
+ if(!frame->arguments_obj) {
+ hres = setup_arguments_object(ctx, frame);
+ if(FAILED(hres))
+ return hres;
+ }
*r = jsval_obj(jsdisp_addref(frame->arguments_obj));
return S_OK;
}
if(FAILED(hres))
return hres;
- if(code->global_code.func_cnt != 1 || code->global_code.var_cnt) {
+ if(code->global_code.func_cnt != 1 || code->global_code.var_cnt != 1) {
ERR("Invalid parser result!\n");
release_bytecode(code);
return E_UNEXPECTED;
jsval_t *r)
{
call_frame_t *frame;
- DWORD exec_flags = 0;
+ DWORD exec_flags = EXEC_EVAL;
bytecode_t *code;
const WCHAR *src;
HRESULT hres;
if(flags & DISPATCH_JSCRIPT_CALLEREXECSSOURCE)
exec_flags |= EXEC_RETURN_TO_INTERP;
hres = exec_source(ctx, exec_flags, code, &code->global_code, frame->scope,
- frame->this_obj, NULL, frame->variable_obj, NULL, r);
+ frame->this_obj, NULL, frame->variable_obj, 0, NULL, r);
release_bytecode(code);
return hres;
}
IActiveScriptSite_OnEnterScript(This->site);
clear_ei(This->ctx);
- hres = exec_source(This->ctx, EXEC_GLOBAL, code, &code->global_code, NULL, NULL, NULL, This->ctx->global, NULL, NULL);
+ hres = exec_source(This->ctx, EXEC_GLOBAL, code, &code->global_code, NULL, NULL, NULL, This->ctx->global, 0, NULL, NULL);
IActiveScriptSite_OnLeaveScript(This->site);
return hres;
IActiveScriptSite_OnEnterScript(This->site);
clear_ei(This->ctx);
- hres = exec_source(This->ctx, EXEC_GLOBAL, code, &code->global_code, NULL, NULL, NULL, This->ctx->global, NULL, &r);
+ hres = exec_source(This->ctx, EXEC_GLOBAL, code, &code->global_code, NULL, NULL, NULL, This->ctx->global, 0, NULL, &r);
if(SUCCEEDED(hres)) {
if(pvarResult)
hres = jsval_to_variant(r, pvarResult);
JSCLASS_JSON
} jsclass_t;
-jsdisp_t *iface_to_jsdisp(IUnknown*) DECLSPEC_HIDDEN;
+jsdisp_t *iface_to_jsdisp(IDispatch*) DECLSPEC_HIDDEN;
typedef struct {
union {
jsdisp_t *jsdisp;
HRESULT hres;
- jsdisp = iface_to_jsdisp((IUnknown*)disp);
+ jsdisp = iface_to_jsdisp(disp);
if(jsdisp) {
vdisp->u.jsdisp = jsdisp;
vdisp->flags = VDISP_JSDISP | VDISP_DISPEX;
jsdisp_t *obj;
HRESULT hres;
- if(!is_object_instance(val) || !get_object(val) || !(obj = iface_to_jsdisp((IUnknown*)get_object(val))))
+ if(!is_object_instance(val) || !get_object(val) || !(obj = iface_to_jsdisp(get_object(val))))
return jsval_copy(val, r);
if(is_class(obj, JSCLASS_NUMBER)) {
jsdisp_t *obj;
DISPID id;
- obj = iface_to_jsdisp((IUnknown*)get_object(val));
+ obj = iface_to_jsdisp(get_object(val));
if(!obj)
return S_FALSE;
case JSV_OBJECT: {
jsdisp_t *obj;
- obj = iface_to_jsdisp((IUnknown*)get_object(value));
+ obj = iface_to_jsdisp(get_object(value));
if(!obj) {
hres = S_FALSE;
break;
TRACE("\n");
+ jsval_release(regexp->last_index_val);
hres = jsval_copy(value, ®exp->last_index_val);
if(FAILED(hres))
return hres;
if(is_object_instance(src_arg)) {
jsdisp_t *obj;
- obj = iface_to_jsdisp((IUnknown*)get_object(src_arg));
+ obj = iface_to_jsdisp(get_object(src_arg));
if(obj) {
if(is_class(obj, JSCLASS_REGEXP)) {
RegExpInstance *regexp = (RegExpInstance*)obj;
case DISPATCH_METHOD:
if(argc) {
if(is_object_instance(argv[0])) {
- jsdisp_t *jsdisp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
+ jsdisp_t *jsdisp = iface_to_jsdisp(get_object(argv[0]));
if(jsdisp) {
if(is_class(jsdisp, JSCLASS_REGEXP)) {
if(argc > 1 && !is_undefined(argv[1])) {
if(!heap)
return;
- while((tmp = list_next(&heap->custom_blocks, &heap->custom_blocks))) {
+ while((tmp = list_head(&heap->custom_blocks))) {
list_remove(tmp);
heap_free(tmp);
}
__JSVAL_TYPE(*val) = JSV_VARIANT;
__JSVAL_VAR(*val) = v = heap_alloc(sizeof(VARIANT));
- if(!v)
+ if(!v) {
+ *val = jsval_undefined();
return E_OUTOFMEMORY;
+ }
V_VT(v) = VT_EMPTY;
hres = VariantCopy(v, var);
- if(FAILED(hres))
+ if(FAILED(hres)) {
+ *val = jsval_undefined();
heap_free(v);
+ }
return hres;
}
return S_OK;
}
- jsdisp = iface_to_jsdisp((IUnknown*)get_object(val));
+ jsdisp = iface_to_jsdisp(get_object(val));
if(!jsdisp)
return disp_propget(ctx, get_object(val), DISPID_VALUE, ret);
switch(flags) {
case DISPATCH_METHOD:
+ case DISPATCH_CONSTRUCT: {
+ jsdisp_t *obj;
+
if(argc) {
if(!is_undefined(argv[0]) && !is_null(argv[0]) && (!is_object_instance(argv[0]) || get_object(argv[0]))) {
IDispatch *disp;
return S_OK;
}
}
- /* fall through */
- case DISPATCH_CONSTRUCT: {
- jsdisp_t *obj;
hres = create_object(ctx, NULL, &obj);
if(FAILED(hres))
source_elements_t *source_elements;
const WCHAR *src_str;
DWORD src_len;
+ unsigned func_id;
struct _function_expression_t *next; /* for compiler */
} function_expression_t;
}
if(is_object_instance(argv[0])) {
- regexp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
+ regexp = iface_to_jsdisp(get_object(argv[0]));
if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
jsdisp_release(regexp);
regexp = NULL;
}
if(is_object_instance(argv[0])) {
- regexp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
+ regexp = iface_to_jsdisp(get_object(argv[0]));
if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
jsdisp_release(regexp);
regexp = NULL;
if(argc >= 2) {
if(is_object_instance(argv[1])) {
- rep_func = iface_to_jsdisp((IUnknown*)get_object(argv[1]));
+ rep_func = iface_to_jsdisp(get_object(argv[1]));
if(rep_func && !is_class(rep_func, JSCLASS_FUNCTION)) {
jsdisp_release(rep_func);
rep_func = NULL;
}
if(is_object_instance(argv[0])) {
- regexp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
+ regexp = iface_to_jsdisp(get_object(argv[0]));
if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
jsdisp_release(regexp);
regexp = NULL;
}
if(is_object_instance(argv[0])) {
- regexp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
+ regexp = iface_to_jsdisp(get_object(argv[0]));
if(regexp) {
if(!is_class(regexp, JSCLASS_REGEXP)) {
jsdisp_release(regexp);
reactos/dll/win32/iphlpapi # Out of sync
reactos/dll/win32/itircl # Synced to WineStaging-1.9.11
reactos/dll/win32/itss # Synced to WineStaging-1.9.11
-reactos/dll/win32/jscript # Synced to WineStaging-1.9.11
+reactos/dll/win32/jscript # Synced to WineStaging-1.9.16
reactos/dll/win32/jsproxy # Synced to WineStaging-1.9.11
reactos/dll/win32/loadperf # Synced to WineStaging-1.9.11
reactos/dll/win32/localspl # Synced to WineStaging-1.9.11