jscript: Support block scope variables.
Signed-off-by: Paul Gofman <pgofman@codeweavers.com> Signed-off-by: Jacek Caban <jacek@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
a35901137e
commit
308c8468b4
|
@ -65,14 +65,13 @@ typedef struct _compiler_ctx_t {
|
|||
|
||||
struct
|
||||
{
|
||||
struct wine_rb_tree locals;
|
||||
unsigned int locals_cnt;
|
||||
unsigned int *ref_index;
|
||||
}
|
||||
*local_scopes;
|
||||
unsigned local_scope_count;
|
||||
unsigned local_scope_size;
|
||||
struct wine_rb_tree locals;
|
||||
unsigned locals_cnt;
|
||||
|
||||
statement_ctx_t *stat_ctx;
|
||||
function_code_t *func;
|
||||
|
@ -137,6 +136,12 @@ static void dump_code(compiler_ctx_t *ctx, unsigned off)
|
|||
static HRESULT compile_expression(compiler_ctx_t*,expression_t*,BOOL);
|
||||
static HRESULT compile_statement(compiler_ctx_t*,statement_ctx_t*,statement_t*);
|
||||
|
||||
static int function_local_cmp(const void *key, const struct wine_rb_entry *entry)
|
||||
{
|
||||
function_local_t *local = WINE_RB_ENTRY_VALUE(entry, function_local_t, entry);
|
||||
return wcscmp(key, local->name);
|
||||
}
|
||||
|
||||
static BOOL alloc_local_scope(compiler_ctx_t *ctx, unsigned int *scope_index)
|
||||
{
|
||||
unsigned int scope, new_size;
|
||||
|
@ -154,6 +159,7 @@ static BOOL alloc_local_scope(compiler_ctx_t *ctx, unsigned int *scope_index)
|
|||
|
||||
ctx->local_scopes[scope].locals_cnt = 0;
|
||||
ctx->local_scopes[scope].ref_index = scope_index;
|
||||
wine_rb_init(&ctx->local_scopes[scope].locals, function_local_cmp);
|
||||
*scope_index = scope;
|
||||
|
||||
return TRUE;
|
||||
|
@ -485,10 +491,19 @@ static BOOL bind_local(compiler_ctx_t *ctx, const WCHAR *identifier, int *ret_re
|
|||
|
||||
for(iter = ctx->stat_ctx; iter; iter = iter->next) {
|
||||
if(iter->using_scope)
|
||||
return FALSE;
|
||||
{
|
||||
if (!iter->block_scope)
|
||||
return FALSE;
|
||||
|
||||
if ((ref = lookup_local(ctx->func, identifier, iter->scope_index)))
|
||||
{
|
||||
*ret_ref = ref->ref;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ref = lookup_local(ctx->func, identifier);
|
||||
ref = lookup_local(ctx->func, identifier, 0);
|
||||
if(!ref)
|
||||
return FALSE;
|
||||
|
||||
|
@ -1157,18 +1172,33 @@ static inline BOOL is_loop_statement(statement_type_t type)
|
|||
}
|
||||
|
||||
/* ECMA-262 3rd Edition 12.1 */
|
||||
static HRESULT compile_block_statement(compiler_ctx_t *ctx, statement_t *iter)
|
||||
static HRESULT compile_block_statement(compiler_ctx_t *ctx, block_statement_t *block, statement_t *iter)
|
||||
{
|
||||
statement_ctx_t stat_ctx = {0, TRUE};
|
||||
BOOL needs_scope;
|
||||
HRESULT hres;
|
||||
|
||||
needs_scope = block && block->scope_index;
|
||||
if (needs_scope)
|
||||
{
|
||||
if(FAILED(hres = push_instr_uint(ctx, OP_push_block_scope, block->scope_index)))
|
||||
return hres;
|
||||
|
||||
stat_ctx.scope_index = block->scope_index;
|
||||
stat_ctx.block_scope = TRUE;
|
||||
}
|
||||
|
||||
while(iter) {
|
||||
hres = compile_statement(ctx, NULL, iter);
|
||||
hres = compile_statement(ctx, needs_scope ? &stat_ctx : NULL, iter);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
iter = iter->next;
|
||||
}
|
||||
|
||||
if(needs_scope && !push_instr(ctx, OP_pop_scope))
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
@ -1184,8 +1214,6 @@ static HRESULT compile_variable_list(compiler_ctx_t *ctx, variable_declaration_t
|
|||
if(!iter->expr)
|
||||
continue;
|
||||
|
||||
if (iter->block_scope)
|
||||
FIXME("Block scope variables are not supported.\n");
|
||||
if (iter->constant)
|
||||
FIXME("Constant variables are not supported.\n");
|
||||
|
||||
|
@ -1595,7 +1623,7 @@ static HRESULT compile_with_statement(compiler_ctx_t *ctx, with_statement_t *sta
|
|||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
if(!push_instr(ctx, OP_push_scope))
|
||||
if(!push_instr(ctx, OP_push_with_scope))
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
hres = compile_statement(ctx, &stat_ctx, stat->statement);
|
||||
|
@ -1833,7 +1861,7 @@ static HRESULT compile_statement(compiler_ctx_t *ctx, statement_ctx_t *stat_ctx,
|
|||
|
||||
switch(stat->type) {
|
||||
case STAT_BLOCK:
|
||||
hres = compile_block_statement(ctx, ((block_statement_t*)stat)->stat_list);
|
||||
hres = compile_block_statement(ctx, (block_statement_t*)stat, ((block_statement_t*)stat)->stat_list);
|
||||
break;
|
||||
case STAT_BREAK:
|
||||
hres = compile_break_statement(ctx, (branch_statement_t*)stat);
|
||||
|
@ -1892,15 +1920,9 @@ static HRESULT compile_statement(compiler_ctx_t *ctx, statement_ctx_t *stat_ctx,
|
|||
return hres;
|
||||
}
|
||||
|
||||
static int function_local_cmp(const void *key, const struct wine_rb_entry *entry)
|
||||
static inline function_local_t *find_local(compiler_ctx_t *ctx, const WCHAR *name, unsigned int scope)
|
||||
{
|
||||
function_local_t *local = WINE_RB_ENTRY_VALUE(entry, function_local_t, entry);
|
||||
return wcscmp(key, local->name);
|
||||
}
|
||||
|
||||
static inline function_local_t *find_local(compiler_ctx_t *ctx, const WCHAR *name)
|
||||
{
|
||||
struct wine_rb_entry *entry = wine_rb_get(&ctx->locals, name);
|
||||
struct wine_rb_entry *entry = wine_rb_get(&ctx->local_scopes[scope].locals, name);
|
||||
return entry ? WINE_RB_ENTRY_VALUE(entry, function_local_t, entry) : NULL;
|
||||
}
|
||||
|
||||
|
@ -1914,8 +1936,7 @@ static BOOL alloc_local(compiler_ctx_t *ctx, BSTR name, int ref, unsigned int sc
|
|||
|
||||
local->name = name;
|
||||
local->ref = ref;
|
||||
wine_rb_put(&ctx->locals, name, &local->entry);
|
||||
ctx->locals_cnt++;
|
||||
wine_rb_put(&ctx->local_scopes[scope].locals, name, &local->entry);
|
||||
ctx->local_scopes[scope].locals_cnt++;
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -1924,7 +1945,7 @@ static BOOL alloc_variable(compiler_ctx_t *ctx, const WCHAR *name, unsigned int
|
|||
{
|
||||
BSTR ident;
|
||||
|
||||
if(find_local(ctx, name))
|
||||
if(find_local(ctx, name, scope))
|
||||
return TRUE;
|
||||
|
||||
ident = compiler_alloc_bstr(ctx, name);
|
||||
|
@ -2416,8 +2437,6 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source,
|
|||
ctx->func_head = ctx->func_tail = NULL;
|
||||
ctx->from_eval = from_eval;
|
||||
ctx->func = func;
|
||||
ctx->locals_cnt = 0;
|
||||
wine_rb_init(&ctx->locals, function_local_cmp);
|
||||
ctx->local_scope_count = 0;
|
||||
if (!alloc_local_scope(ctx, &scope))
|
||||
return E_OUTOFMEMORY;
|
||||
|
@ -2456,7 +2475,7 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source,
|
|||
}
|
||||
|
||||
for(i = 0; i < func->param_cnt; i++) {
|
||||
if(!find_local(ctx, func->params[i]) && !alloc_local(ctx, func->params[i], -i-1, 0))
|
||||
if(!find_local(ctx, func->params[i], 0) && !alloc_local(ctx, func->params[i], -i-1, 0))
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
|
@ -2464,30 +2483,35 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source,
|
|||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
func->local_scope_count = 1;
|
||||
func->local_scope_count = ctx->local_scope_count;
|
||||
func->local_scopes = compiler_alloc(ctx->code, func->local_scope_count * sizeof(*func->local_scopes));
|
||||
if(!func->local_scopes)
|
||||
return E_OUTOFMEMORY;
|
||||
func->local_scopes[0].locals = compiler_alloc(ctx->code, ctx->locals_cnt * sizeof(*func->local_scopes[0].locals));
|
||||
if(!func->local_scopes[0].locals)
|
||||
return E_OUTOFMEMORY;
|
||||
func->local_scopes[0].locals_cnt = ctx->locals_cnt;
|
||||
|
||||
func->variables = compiler_alloc(ctx->code, func->var_cnt * sizeof(*func->variables));
|
||||
if(!func->variables)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
i = 0;
|
||||
WINE_RB_FOR_EACH_ENTRY(local, &ctx->locals, function_local_t, entry) {
|
||||
func->local_scopes[0].locals[i].name = local->name;
|
||||
func->local_scopes[0].locals[i].ref = local->ref;
|
||||
if(local->ref >= 0) {
|
||||
func->variables[local->ref].name = local->name;
|
||||
func->variables[local->ref].func_id = -1;
|
||||
for (scope = 0; scope < func->local_scope_count; ++scope)
|
||||
{
|
||||
func->local_scopes[scope].locals = compiler_alloc(ctx->code,
|
||||
ctx->local_scopes[scope].locals_cnt * sizeof(*func->local_scopes[scope].locals));
|
||||
if(!func->local_scopes[scope].locals)
|
||||
return E_OUTOFMEMORY;
|
||||
func->local_scopes[scope].locals_cnt = ctx->local_scopes[scope].locals_cnt;
|
||||
|
||||
i = 0;
|
||||
WINE_RB_FOR_EACH_ENTRY(local, &ctx->local_scopes[scope].locals, function_local_t, entry) {
|
||||
func->local_scopes[scope].locals[i].name = local->name;
|
||||
func->local_scopes[scope].locals[i].ref = local->ref;
|
||||
if(local->ref >= 0) {
|
||||
func->variables[local->ref].name = local->name;
|
||||
func->variables[local->ref].func_id = -1;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
assert(i == ctx->local_scopes[scope].locals_cnt);
|
||||
}
|
||||
assert(i == ctx->locals_cnt);
|
||||
|
||||
func->funcs = compiler_alloc(ctx->code, func->func_cnt * sizeof(*func->funcs));
|
||||
if(!func->funcs)
|
||||
|
@ -2495,7 +2519,7 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source,
|
|||
memset(func->funcs, 0, func->func_cnt * sizeof(*func->funcs));
|
||||
|
||||
off = ctx->code_off;
|
||||
hres = compile_block_statement(ctx, source->statement);
|
||||
hres = compile_block_statement(ctx, NULL, source->statement);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
|
@ -2518,7 +2542,7 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source,
|
|||
TRACE("[%d] func %s\n", i, debugstr_w(func->funcs[i].name));
|
||||
if((ctx->parser->script->version < SCRIPTLANGUAGEVERSION_ES5 || iter->is_statement) &&
|
||||
func->funcs[i].name && !func->funcs[i].event_target) {
|
||||
local_ref_t *local_ref = lookup_local(func, func->funcs[i].name);
|
||||
local_ref_t *local_ref = lookup_local(func, func->funcs[i].name, 0);
|
||||
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)
|
||||
|
|
|
@ -205,30 +205,45 @@ static BOOL stack_topn_exprval(script_ctx_t *ctx, unsigned n, exprval_t *r)
|
|||
switch(jsval_type(v)) {
|
||||
case JSV_NUMBER: {
|
||||
call_frame_t *frame = ctx->call_ctx;
|
||||
scope_chain_t *scope;
|
||||
unsigned off = get_number(v);
|
||||
|
||||
if(!frame->base_scope->frame && off >= frame->arguments_off) {
|
||||
DISPID id;
|
||||
BSTR name;
|
||||
HRESULT hres;
|
||||
HRESULT hres = E_FAIL;
|
||||
|
||||
/* 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;
|
||||
if (off >= frame->variables_off)
|
||||
{
|
||||
name = frame->function->variables[off - frame->variables_off].name;
|
||||
scope = frame->scope;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = frame->function->params[off - frame->arguments_off];
|
||||
scope = frame->base_scope;
|
||||
}
|
||||
|
||||
*stack_top_ref(ctx, n+1) = jsval_obj(jsdisp_addref(frame->base_scope->jsobj));
|
||||
while (1)
|
||||
{
|
||||
if (scope->jsobj && SUCCEEDED(hres = jsdisp_get_id(scope->jsobj, name, 0, &id)))
|
||||
break;
|
||||
if (scope == frame->base_scope)
|
||||
{
|
||||
r->type = EXPRVAL_INVALID;
|
||||
r->u.hres = hres;
|
||||
return FALSE;
|
||||
}
|
||||
scope = scope->next;
|
||||
}
|
||||
|
||||
*stack_top_ref(ctx, n+1) = jsval_obj(jsdisp_addref(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.disp = scope->obj;
|
||||
r->u.idref.id = id;
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -401,11 +416,13 @@ static HRESULT scope_push(scope_chain_t *scope, jsdisp_t *jsobj, IDispatch *obj,
|
|||
|
||||
new_scope->ref = 1;
|
||||
|
||||
IDispatch_AddRef(obj);
|
||||
if (obj)
|
||||
IDispatch_AddRef(obj);
|
||||
new_scope->jsobj = jsobj;
|
||||
new_scope->obj = obj;
|
||||
new_scope->frame = NULL;
|
||||
new_scope->next = scope ? scope_addref(scope) : NULL;
|
||||
new_scope->scope_index = 0;
|
||||
|
||||
*ret = new_scope;
|
||||
return S_OK;
|
||||
|
@ -428,7 +445,8 @@ void scope_release(scope_chain_t *scope)
|
|||
if(scope->next)
|
||||
scope_release(scope->next);
|
||||
|
||||
IDispatch_Release(scope->obj);
|
||||
if (scope->obj)
|
||||
IDispatch_Release(scope->obj);
|
||||
heap_free(scope);
|
||||
}
|
||||
|
||||
|
@ -553,13 +571,59 @@ HRESULT jsval_strict_equal(jsval_t lval, jsval_t rval, BOOL *ret)
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT detach_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_t *scope)
|
||||
{
|
||||
unsigned int i, index;
|
||||
HRESULT hres;
|
||||
|
||||
if (!scope->frame)
|
||||
return S_OK;
|
||||
|
||||
assert(scope->frame == frame);
|
||||
scope->frame = NULL;
|
||||
|
||||
if (!scope->jsobj)
|
||||
{
|
||||
assert(!scope->obj);
|
||||
|
||||
if (FAILED(hres = create_object(ctx, NULL, &scope->jsobj)))
|
||||
return hres;
|
||||
scope->obj = to_disp(scope->jsobj);
|
||||
}
|
||||
|
||||
index = scope->scope_index;
|
||||
for(i = 0; i < frame->function->local_scopes[index].locals_cnt; i++)
|
||||
{
|
||||
WCHAR *name = frame->function->local_scopes[index].locals[i].name;
|
||||
int ref = frame->function->local_scopes[index].locals[i].ref;
|
||||
|
||||
if (FAILED(hres = jsdisp_propput_name(scope->jsobj, name, ctx->stack[local_off(frame, ref)])))
|
||||
return hres;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT detach_scope_chain(script_ctx_t *ctx, call_frame_t *frame, scope_chain_t *scope)
|
||||
{
|
||||
HRESULT hres;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if ((hres = detach_scope(ctx, frame, scope)))
|
||||
return hres;
|
||||
if (scope == frame->base_scope)
|
||||
break;
|
||||
scope = scope->next;
|
||||
}
|
||||
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)
|
||||
|
@ -576,15 +640,8 @@ static HRESULT detach_variable_object(script_ctx_t *ctx, call_frame_t *frame, BO
|
|||
return hres;
|
||||
}
|
||||
|
||||
frame->base_scope->frame = NULL;
|
||||
|
||||
for(i = 0; i < frame->function->local_scopes[0].locals_cnt; i++) {
|
||||
hres = jsdisp_propput_name(frame->variable_obj, frame->function->local_scopes[0].locals[i].name,
|
||||
ctx->stack[local_off(frame, frame->function->local_scopes[0].locals[i].ref)]);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
}
|
||||
return S_OK;
|
||||
TRACE("detaching scope chain %p, frame %p.\n", ctx->call_ctx->scope, frame);
|
||||
return detach_scope_chain(ctx, frame, ctx->call_ctx->scope);
|
||||
}
|
||||
|
||||
static BOOL lookup_global_members(script_ctx_t *ctx, BSTR identifier, exprval_t *ret)
|
||||
|
@ -627,10 +684,10 @@ static int __cdecl local_ref_cmp(const void *key, const void *ref)
|
|||
return wcscmp((const WCHAR*)key, ((const local_ref_t*)ref)->name);
|
||||
}
|
||||
|
||||
local_ref_t *lookup_local(const function_code_t *function, const WCHAR *identifier)
|
||||
local_ref_t *lookup_local(const function_code_t *function, const WCHAR *identifier, unsigned int scope)
|
||||
{
|
||||
return bsearch(identifier, function->local_scopes[0].locals, function->local_scopes[0].locals_cnt,
|
||||
sizeof(*function->local_scopes[0].locals), local_ref_cmp);
|
||||
return bsearch(identifier, function->local_scopes[scope].locals, function->local_scopes[scope].locals_cnt,
|
||||
sizeof(*function->local_scopes[scope].locals), local_ref_cmp);
|
||||
}
|
||||
|
||||
/* ECMA-262 3rd Edition 10.1.4 */
|
||||
|
@ -647,7 +704,7 @@ static HRESULT identifier_eval(script_ctx_t *ctx, BSTR identifier, exprval_t *re
|
|||
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);
|
||||
local_ref_t *ref = lookup_local(func, identifier, scope->scope_index);
|
||||
|
||||
if(ref) {
|
||||
ret->type = EXPRVAL_STACK_REF;
|
||||
|
@ -670,6 +727,10 @@ static HRESULT identifier_eval(script_ctx_t *ctx, BSTR identifier, exprval_t *re
|
|||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (!scope->jsobj && !scope->obj)
|
||||
continue;
|
||||
|
||||
if(scope->jsobj)
|
||||
hres = jsdisp_get_id(scope->jsobj, identifier, fdexNameImplicit, &id);
|
||||
else
|
||||
|
@ -820,8 +881,54 @@ static HRESULT interp_forin(script_ctx_t *ctx)
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT scope_init_locals(script_ctx_t *ctx)
|
||||
{
|
||||
call_frame_t *frame = ctx->call_ctx;
|
||||
unsigned int i, off, index;
|
||||
scope_chain_t *scope;
|
||||
BOOL detached_vars;
|
||||
HRESULT hres;
|
||||
|
||||
scope = frame->scope;
|
||||
index = scope->scope_index;
|
||||
detached_vars = !(frame->base_scope && frame->base_scope->frame);
|
||||
|
||||
if (!detached_vars)
|
||||
{
|
||||
assert(frame->base_scope->frame == frame);
|
||||
frame->scope->frame = ctx->call_ctx;
|
||||
}
|
||||
else if (!scope->jsobj)
|
||||
{
|
||||
assert(!scope->obj);
|
||||
if (FAILED(hres = create_object(ctx, NULL, &scope->jsobj)))
|
||||
return hres;
|
||||
scope->obj = to_disp(scope->jsobj);
|
||||
}
|
||||
|
||||
for(i = 0; i < frame->function->local_scopes[index].locals_cnt; i++)
|
||||
{
|
||||
WCHAR *name = frame->function->local_scopes[index].locals[i].name;
|
||||
int ref = frame->function->local_scopes[index].locals[i].ref;
|
||||
jsval_t val = jsval_undefined();
|
||||
|
||||
if (detached_vars)
|
||||
{
|
||||
if (FAILED(hres = jsdisp_propput_name(scope->jsobj, name, val)))
|
||||
return hres;
|
||||
}
|
||||
else
|
||||
{
|
||||
off = local_off(frame, ref);
|
||||
jsval_release(ctx->stack[off]);
|
||||
ctx->stack[off] = val;
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* ECMA-262 3rd Edition 12.10 */
|
||||
static HRESULT interp_push_scope(script_ctx_t *ctx)
|
||||
static HRESULT interp_push_with_scope(script_ctx_t *ctx)
|
||||
{
|
||||
IDispatch *disp;
|
||||
jsval_t v;
|
||||
|
@ -840,6 +947,25 @@ static HRESULT interp_push_scope(script_ctx_t *ctx)
|
|||
return hres;
|
||||
}
|
||||
|
||||
/* ECMA-262 10th Edition 13.3.1 */
|
||||
static HRESULT interp_push_block_scope(script_ctx_t *ctx)
|
||||
{
|
||||
unsigned int scope_index = get_op_uint(ctx, 0);
|
||||
call_frame_t *frame = ctx->call_ctx;
|
||||
HRESULT hres;
|
||||
|
||||
TRACE("scope_index %u.\n", scope_index);
|
||||
|
||||
hres = scope_push(ctx->call_ctx->scope, NULL, NULL, &frame->scope);
|
||||
|
||||
if (FAILED(hres) || !scope_index)
|
||||
return hres;
|
||||
|
||||
frame->scope->scope_index = scope_index;
|
||||
|
||||
return scope_init_locals(ctx);
|
||||
}
|
||||
|
||||
/* ECMA-262 3rd Edition 12.10 */
|
||||
static HRESULT interp_pop_scope(script_ctx_t *ctx)
|
||||
{
|
||||
|
|
|
@ -75,7 +75,8 @@
|
|||
X(preinc, 1, ARG_INT, 0) \
|
||||
X(push_acc, 1, 0,0) \
|
||||
X(push_except,1, ARG_ADDR, ARG_UINT) \
|
||||
X(push_scope, 1, 0,0) \
|
||||
X(push_block_scope, 1, ARG_UINT, 0) \
|
||||
X(push_with_scope, 1, 0,0) \
|
||||
X(regexp, 1, ARG_STR, ARG_UINT) \
|
||||
X(rshift, 1, 0,0) \
|
||||
X(rshift2, 1, 0,0) \
|
||||
|
@ -180,7 +181,7 @@ typedef struct _function_code_t {
|
|||
} function_code_t;
|
||||
|
||||
IDispatch *lookup_global_host(script_ctx_t*) DECLSPEC_HIDDEN;
|
||||
local_ref_t *lookup_local(const function_code_t*,const WCHAR*) DECLSPEC_HIDDEN;
|
||||
local_ref_t *lookup_local(const function_code_t*,const WCHAR*,unsigned int) DECLSPEC_HIDDEN;
|
||||
|
||||
struct _bytecode_t {
|
||||
LONG ref;
|
||||
|
@ -222,6 +223,7 @@ typedef struct _scope_chain_t {
|
|||
LONG ref;
|
||||
jsdisp_t *jsobj;
|
||||
IDispatch *obj;
|
||||
unsigned int scope_index;
|
||||
struct _call_frame_t *frame;
|
||||
struct _scope_chain_t *next;
|
||||
} scope_chain_t;
|
||||
|
|
|
@ -1586,6 +1586,22 @@ tmp.testWith = true;
|
|||
with(tmp)
|
||||
ok(testWith === true, "testWith !== true");
|
||||
|
||||
function withScopeTest()
|
||||
{
|
||||
var a = 3;
|
||||
with({a : 2})
|
||||
{
|
||||
ok(a == 2, "withScopeTest: a != 2");
|
||||
function func()
|
||||
{
|
||||
ok(a == 3, "withScopeTest: func: a != 3");
|
||||
}
|
||||
func();
|
||||
eval('ok(a == 2, "withScopeTest: eval: a != 2");');
|
||||
}
|
||||
}
|
||||
withScopeTest();
|
||||
|
||||
if(false) {
|
||||
var varTest1 = true;
|
||||
}
|
||||
|
|
|
@ -1210,17 +1210,63 @@ sync_test("head_setter", function() {
|
|||
|
||||
|
||||
sync_test("declaration_let", function() {
|
||||
ok(typeof(func) === "undefined", "typeof(func) = " + typeof(func));
|
||||
with(new Object()) {
|
||||
var x = false && function func() {};
|
||||
}
|
||||
ok(typeof(func) === "undefined", "typeof(func) = " + typeof(func));
|
||||
|
||||
function expect_exception(func, todo) {
|
||||
try {
|
||||
func();
|
||||
}catch(e) {
|
||||
return;
|
||||
}
|
||||
if (typeof todo === 'undefined' || !todo)
|
||||
ok(false, "expected exception");
|
||||
else
|
||||
todo_wine.ok(false, "expected exception");
|
||||
}
|
||||
|
||||
function call_func(f, expected_a)
|
||||
{
|
||||
f(2, expected_a);
|
||||
}
|
||||
|
||||
ok(a === undefined, "a is not undefined");
|
||||
var a = 3;
|
||||
|
||||
{
|
||||
let a = 2;
|
||||
let b
|
||||
|
||||
ok(typeof b === 'undefined', "b is defined");
|
||||
ok(b === undefined, "b !== undefined");
|
||||
|
||||
ok(a == 2, "a != 2");
|
||||
|
||||
a = 4;
|
||||
ok(a == 4, "a != 4");
|
||||
|
||||
eval('ok(a == 4, "eval: a != 4"); b = a; a = 5;')
|
||||
ok(b == 4, "b != 4");
|
||||
ok(a == 5, "a != 5");
|
||||
|
||||
function func1()
|
||||
{
|
||||
ok(typeof b === 'undefined', "func1: b is defined");
|
||||
ok(b === undefined, "func1: should produce exception");
|
||||
let b = 1;
|
||||
}
|
||||
expect_exception(func1, true);
|
||||
|
||||
function func2()
|
||||
{
|
||||
let b = 1;
|
||||
ok(b == 1, "func2: b != 1");
|
||||
}
|
||||
func2();
|
||||
}
|
||||
|
||||
todo_wine.ok(a == 3, "a != 3");
|
||||
ok(a == 3, "a != 3");
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue