[PATCH 3/4] jscript: Support block scope variables.
Paul Gofman
pgofman at codeweavers.com
Fri Jun 11 12:46:25 CDT 2021
Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
dlls/jscript/compile.c | 234 +++++++++++++++++++++++++++----------
dlls/jscript/engine.c | 105 ++++++++++++++---
dlls/jscript/engine.h | 5 +-
dlls/jscript/parser.h | 2 +
dlls/jscript/parser.y | 2 +
dlls/jscript/tests/lang.js | 16 +++
dlls/mshtml/tests/es5.js | 56 ++++++++-
7 files changed, 341 insertions(+), 79 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c
index 46efd53cdb5..1227a27ddcc 100644
--- a/dlls/jscript/compile.c
+++ b/dlls/jscript/compile.c
@@ -39,6 +39,8 @@ typedef struct _statement_ctx_t {
const labelled_statement_t *labelled_stat;
+ unsigned int scope_index;
+ BOOL block_scope;
struct _statement_ctx_t *next;
} statement_ctx_t;
@@ -48,6 +50,8 @@ typedef struct {
int ref;
} function_local_t;
+#define MAX_SCOPE_COUNT 1024
+
typedef struct _compiler_ctx_t {
parser_ctx_t *parser;
bytecode_t *code;
@@ -61,8 +65,14 @@ typedef struct _compiler_ctx_t {
unsigned labels_size;
unsigned labels_cnt;
- struct wine_rb_tree locals;
- unsigned locals_cnt;
+ struct
+ {
+ struct wine_rb_tree locals;
+ unsigned int locals_cnt;
+ unsigned int *ref_index;
+ }
+ *local_scopes;
+ unsigned local_scope_count;
statement_ctx_t *stat_ctx;
function_code_t *func;
@@ -253,6 +263,19 @@ static HRESULT push_instr_int(compiler_ctx_t *ctx, jsop_t op, LONG arg)
return S_OK;
}
+static HRESULT push_instr_uint_uint(compiler_ctx_t *ctx, jsop_t op, unsigned arg1, unsigned arg2)
+{
+ unsigned instr;
+
+ instr = push_instr(ctx, op);
+ if(!instr)
+ return E_OUTOFMEMORY;
+
+ instr_ptr(ctx, instr)->u.arg[0].uint = arg1;
+ instr_ptr(ctx, instr)->u.arg[1].uint = arg2;
+ return S_OK;
+}
+
static HRESULT push_instr_str(compiler_ctx_t *ctx, jsop_t op, jsstr_t *str)
{
unsigned instr;
@@ -439,10 +462,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;
+ TRACE("iter->scope_index %d, ctx->func->local_scope_count %d.\n", iter->scope_index, ctx->func->local_scope_count);
+ 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;
@@ -1111,18 +1143,35 @@ 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(!push_instr(ctx, OP_new_obj))
+ return E_OUTOFMEMORY;
+ if(FAILED(hres = push_instr_uint_uint(ctx, OP_push_scope, block->scope_index, TRUE)))
+ 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;
}
@@ -1138,8 +1187,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");
@@ -1549,8 +1596,8 @@ 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))
- return E_OUTOFMEMORY;
+ if(FAILED(hres = push_instr_uint_uint(ctx, OP_push_scope, stat->scope_index, FALSE)))
+ return hres;
hres = compile_statement(ctx, &stat_ctx, stat->statement);
if(FAILED(hres))
@@ -1787,7 +1834,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);
@@ -1852,13 +1899,13 @@ static int function_local_cmp(const void *key, const struct wine_rb_entry *entry
return wcscmp(key, local->name);
}
-static inline function_local_t *find_local(compiler_ctx_t *ctx, const WCHAR *name)
+static inline function_local_t *find_local(compiler_ctx_t *ctx, const WCHAR *name, unsigned int scope)
{
- 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;
}
-static BOOL alloc_local(compiler_ctx_t *ctx, BSTR name, int ref)
+static BOOL alloc_local(compiler_ctx_t *ctx, BSTR name, int ref, unsigned int scope)
{
function_local_t *local;
@@ -1868,23 +1915,23 @@ static BOOL alloc_local(compiler_ctx_t *ctx, BSTR name, int ref)
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;
}
-static BOOL alloc_variable(compiler_ctx_t *ctx, const WCHAR *name)
+static BOOL alloc_variable(compiler_ctx_t *ctx, const WCHAR *name, unsigned int scope)
{
BSTR ident;
- if(find_local(ctx, name))
+ if(find_local(ctx, name, scope))
return TRUE;
ident = compiler_alloc_bstr(ctx, name);
if(!ident)
return FALSE;
- return alloc_local(ctx, ident, ctx->func->var_cnt++);
+ return alloc_local(ctx, ident, ctx->func->var_cnt++, scope);
}
static HRESULT visit_function_expression(compiler_ctx_t *ctx, function_expression_t *expr)
@@ -1897,7 +1944,7 @@ static HRESULT visit_function_expression(compiler_ctx_t *ctx, function_expressio
if(!expr->is_statement && ctx->parser->script->version >= SCRIPTLANGUAGEVERSION_ES5)
return S_OK;
- return alloc_variable(ctx, expr->identifier) ? S_OK : E_OUTOFMEMORY;
+ return alloc_variable(ctx, expr->identifier, 0) ? S_OK : E_OUTOFMEMORY;
}
static HRESULT visit_expression(compiler_ctx_t *ctx, expression_t *expr)
@@ -2033,11 +2080,18 @@ static HRESULT visit_expression(compiler_ctx_t *ctx, expression_t *expr)
static HRESULT visit_variable_list(compiler_ctx_t *ctx, variable_declaration_t *list)
{
variable_declaration_t *iter;
+ statement_ctx_t *stat_ctx;
HRESULT hres;
for(iter = list; iter; iter = iter->next) {
- if(!alloc_variable(ctx, iter->identifier))
- return E_OUTOFMEMORY;
+ for (stat_ctx = ctx->stat_ctx; stat_ctx; stat_ctx = stat_ctx->next)
+ {
+ if (stat_ctx->block_scope)
+ break;
+ }
+
+ if(!alloc_variable(ctx, iter->identifier, iter->block_scope && stat_ctx ? stat_ctx->scope_index : 0))
+ return E_OUTOFMEMORY;
if(iter->expr) {
hres = visit_expression(ctx, iter->expr);
@@ -2049,30 +2103,67 @@ static HRESULT visit_variable_list(compiler_ctx_t *ctx, variable_declaration_t *
return S_OK;
}
-static HRESULT visit_statement(compiler_ctx_t*,statement_t*);
+static HRESULT visit_statement(compiler_ctx_t*,statement_ctx_t *,statement_t*);
-static HRESULT visit_block_statement(compiler_ctx_t *ctx, statement_t *iter)
+static HRESULT visit_block_statement(compiler_ctx_t *ctx, block_statement_t *block, statement_t *iter)
{
+ statement_ctx_t stat_ctx = {0, TRUE};
+ BOOL needs_scope;
+ unsigned int i;
HRESULT hres;
+ needs_scope = block && ctx->parser->script->version >= SCRIPTLANGUAGEVERSION_ES5;
+ if (needs_scope)
+ {
+ if (ctx->local_scope_count > MAX_SCOPE_COUNT)
+ return E_OUTOFMEMORY;
+
+ stat_ctx.scope_index = ctx->local_scope_count++;
+ stat_ctx.block_scope = TRUE;
+ ctx->local_scopes[stat_ctx.scope_index].locals_cnt = 0;
+ ctx->local_scopes[stat_ctx.scope_index].ref_index = &block->scope_index;
+
+ wine_rb_init(&ctx->local_scopes[stat_ctx.scope_index].locals, function_local_cmp);
+ }
+
while(iter) {
- hres = visit_statement(ctx, iter);
+ hres = visit_statement(ctx, needs_scope ? &stat_ctx : NULL, iter);
if(FAILED(hres))
return hres;
iter = iter->next;
}
+ if (!needs_scope)
+ return S_OK;
+
+ if (ctx->local_scopes[stat_ctx.scope_index].locals_cnt)
+ {
+ block->scope_index = stat_ctx.scope_index;
+ }
+ else
+ {
+ --ctx->local_scope_count;
+ memmove(&ctx->local_scopes[stat_ctx.scope_index], &ctx->local_scopes[stat_ctx.scope_index + 1],
+ sizeof(*ctx->local_scopes) * (ctx->local_scope_count - stat_ctx.scope_index));
+ for (i = stat_ctx.scope_index; i < ctx->local_scope_count; ++i)
+ --*ctx->local_scopes[i].ref_index;
+ }
return S_OK;
}
-static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat)
+static HRESULT visit_statement(compiler_ctx_t *ctx, statement_ctx_t *stat_ctx, statement_t *stat)
{
HRESULT hres = S_OK;
+ if(stat_ctx) {
+ stat_ctx->next = ctx->stat_ctx;
+ ctx->stat_ctx = stat_ctx;
+ }
+
switch(stat->type) {
case STAT_BLOCK:
- hres = visit_block_statement(ctx, ((block_statement_t*)stat)->stat_list);
+ hres = visit_block_statement(ctx, (block_statement_t*)stat, ((block_statement_t*)stat)->stat_list);
break;
case STAT_BREAK:
case STAT_CONTINUE:
@@ -2110,7 +2201,7 @@ static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat)
break;
}
- hres = visit_statement(ctx, for_stat->statement);
+ hres = visit_statement(ctx, NULL, for_stat->statement);
if(FAILED(hres))
break;
@@ -2137,7 +2228,7 @@ static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat)
return hres;
}
- hres = visit_statement(ctx, forin_stat->statement);
+ hres = visit_statement(ctx, NULL, forin_stat->statement);
break;
}
case STAT_IF: {
@@ -2147,16 +2238,16 @@ static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat)
if(FAILED(hres))
return hres;
- hres = visit_statement(ctx, if_stat->if_stat);
+ hres = visit_statement(ctx, NULL, if_stat->if_stat);
if(FAILED(hres))
return hres;
if(if_stat->else_stat)
- hres = visit_statement(ctx, if_stat->else_stat);
+ hres = visit_statement(ctx, NULL, if_stat->else_stat);
break;
}
case STAT_LABEL:
- hres = visit_statement(ctx, ((labelled_statement_t*)stat)->statement);
+ hres = visit_statement(ctx, NULL, ((labelled_statement_t*)stat)->statement);
break;
case STAT_SWITCH: {
switch_statement_t *switch_stat = (switch_statement_t*)stat;
@@ -2180,7 +2271,7 @@ static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *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);
+ hres = visit_statement(ctx, NULL, stat_iter);
if(FAILED(hres))
return hres;
}
@@ -2190,18 +2281,18 @@ static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat)
case STAT_TRY: {
try_statement_t *try_stat = (try_statement_t*)stat;
- hres = visit_statement(ctx, try_stat->try_statement);
+ hres = visit_statement(ctx, NULL, try_stat->try_statement);
if(FAILED(hres))
return hres;
if(try_stat->catch_block) {
- hres = visit_statement(ctx, try_stat->catch_block->statement);
+ hres = visit_statement(ctx, NULL, try_stat->catch_block->statement);
if(FAILED(hres))
return hres;
}
if(try_stat->finally_statement)
- hres = visit_statement(ctx, try_stat->finally_statement);
+ hres = visit_statement(ctx, NULL, try_stat->finally_statement);
break;
}
case STAT_VAR:
@@ -2214,22 +2305,35 @@ static HRESULT visit_statement(compiler_ctx_t *ctx, statement_t *stat)
if(FAILED(hres))
return hres;
- hres = visit_statement(ctx, while_stat->statement);
+ hres = visit_statement(ctx, NULL, while_stat->statement);
break;
}
case STAT_WITH: {
with_statement_t *with_stat = (with_statement_t*)stat;
+ statement_ctx_t stat_ctx = {0, TRUE};
hres = visit_expression(ctx, with_stat->expr);
if(FAILED(hres))
return hres;
- hres = visit_statement(ctx, with_stat->statement);
+ if (ctx->parser->script->version >= SCRIPTLANGUAGEVERSION_ES5)
+ {
+ stat_ctx.scope_index = with_stat->scope_index = ctx->local_scope_count++;
+ ctx->local_scopes[stat_ctx.scope_index].locals_cnt = 0;
+ ctx->local_scopes[stat_ctx.scope_index].ref_index = &with_stat->scope_index;
+ }
+
+ hres = visit_statement(ctx, &stat_ctx, with_stat->statement);
break;
}
DEFAULT_UNREACHABLE;
}
+ if(stat_ctx) {
+ assert(ctx->stat_ctx == stat_ctx);
+ ctx->stat_ctx = stat_ctx->next;
+ }
+
return hres;
}
@@ -2325,7 +2429,7 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source,
{
function_expression_t *iter;
function_local_t *local;
- unsigned off, i;
+ unsigned off, i, scope;
HRESULT hres;
TRACE("\n");
@@ -2335,8 +2439,9 @@ 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 = 1;
+ ctx->local_scopes[0].locals_cnt = 0;
+ wine_rb_init(&ctx->local_scopes[0].locals, function_local_cmp);
if(func_expr) {
parameter_t *param_iter;
@@ -2371,38 +2476,43 @@ 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))
+ if(!find_local(ctx, func->params[i], 0) && !alloc_local(ctx, func->params[i], -i-1, 0))
return E_OUTOFMEMORY;
}
- hres = visit_block_statement(ctx, source->statement);
+ hres = visit_block_statement(ctx, NULL, source->statement);
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)
@@ -2410,7 +2520,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;
@@ -2433,7 +2543,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)
@@ -2548,7 +2658,11 @@ HRESULT compile_script(script_ctx_t *ctx, const WCHAR *code, UINT64 source_conte
}
heap_pool_init(&compiler.heap);
- hres = compile_function(&compiler, compiler.parser->source, NULL, from_eval, &compiler.code->global_code);
+ if (!(compiler.local_scopes = heap_alloc(MAX_SCOPE_COUNT * sizeof(*compiler.local_scopes))))
+ hres = E_OUTOFMEMORY;
+ else
+ hres = compile_function(&compiler, compiler.parser->source, NULL, from_eval, &compiler.code->global_code);
+ heap_free(compiler.local_scopes);
heap_pool_free(&compiler.heap);
parser_release(compiler.parser);
if(FAILED(hres)) {
diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c
index c6ca3a9c10d..877d67afb31 100644
--- a/dlls/jscript/engine.c
+++ b/dlls/jscript/engine.c
@@ -205,6 +205,7 @@ 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) {
@@ -218,17 +219,26 @@ static BOOL stack_topn_exprval(script_ctx_t *ctx, unsigned n, exprval_t *r)
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);
+ scope = ctx->call_ctx->scope;
+ hres = E_FAIL;
+ do
+ {
+ if (scope->jsobj && SUCCEEDED(hres = jsdisp_get_id(scope->jsobj, name, 0, &id)))
+ break;
+ scope = scope->next;
+ }
+ while (scope != ctx->call_ctx->base_scope);
+
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+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;
}
@@ -391,7 +401,8 @@ static inline void clear_acc(script_ctx_t *ctx)
ctx->acc = jsval_undefined();
}
-static HRESULT scope_push(scope_chain_t *scope, jsdisp_t *jsobj, IDispatch *obj, scope_chain_t **ret)
+static HRESULT scope_push(scope_chain_t *scope, jsdisp_t *jsobj, IDispatch *obj,
+ unsigned int scope_index, scope_chain_t **ret)
{
scope_chain_t *new_scope;
@@ -406,6 +417,7 @@ static HRESULT scope_push(scope_chain_t *scope, jsdisp_t *jsobj, IDispatch *obj,
new_scope->obj = obj;
new_scope->frame = NULL;
new_scope->next = scope ? scope_addref(scope) : NULL;
+ new_scope->scope_index = scope_index;
*ret = new_scope;
return S_OK;
@@ -559,10 +571,35 @@ HRESULT jsval_strict_equal(jsval_t lval, jsval_t rval, BOOL *ret)
*/
static HRESULT detach_variable_object(script_ctx_t *ctx, call_frame_t *frame, BOOL from_release)
{
- unsigned i;
+ unsigned int i, index;
+ scope_chain_t *scope;
HRESULT hres;
- if(!frame->base_scope || !frame->base_scope->frame)
+ if(!frame->base_scope)
+ return S_OK;
+
+ TRACE("detaching scope chain %p, frame %p.\n", ctx->call_ctx->scope, frame);
+
+ for (scope = ctx->call_ctx->scope; scope != frame->base_scope; scope = scope->next)
+ {
+ if (!scope->frame)
+ continue;
+
+ assert(scope->frame == frame);
+ scope->frame = NULL;
+
+ index = scope->scope_index;
+
+ for(i = 0; i < frame->function->local_scopes[index].locals_cnt; i++)
+ {
+ hres = jsdisp_propput_name(scope->jsobj, frame->function->local_scopes[index].locals[i].name,
+ ctx->stack[local_off(frame, frame->function->local_scopes[index].locals[i].ref)]);
+ if(FAILED(hres))
+ return hres;
+ }
+ }
+
+ if(!frame->base_scope->frame)
return S_OK;
TRACE("detaching %p\n", frame);
@@ -627,10 +664,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 +684,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;
@@ -823,11 +860,17 @@ static HRESULT interp_forin(script_ctx_t *ctx)
/* ECMA-262 3rd Edition 12.10 */
static HRESULT interp_push_scope(script_ctx_t *ctx)
{
+ unsigned int scope_index = get_op_uint(ctx, 0);
+ BOOL scope_block = get_op_uint(ctx, 1);
+ call_frame_t *frame = ctx->call_ctx;
+ unsigned int off;
+ jsdisp_t *dispex;
IDispatch *disp;
- jsval_t v;
+ unsigned int i;
HRESULT hres;
+ jsval_t v;
- TRACE("\n");
+ TRACE("scope_index %u.\n", scope_index);
v = stack_pop(ctx);
hres = to_object(ctx, v, &disp);
@@ -835,9 +878,39 @@ static HRESULT interp_push_scope(script_ctx_t *ctx)
if(FAILED(hres))
return hres;
- hres = scope_push(ctx->call_ctx->scope, to_jsdisp(disp), disp, &ctx->call_ctx->scope);
+ dispex = to_jsdisp(disp);
+ hres = scope_push(ctx->call_ctx->scope, dispex, disp, scope_index, &ctx->call_ctx->scope);
IDispatch_Release(disp);
- return hres;
+
+ if (FAILED(hres) || !scope_block)
+ return hres;
+
+ assert(dispex);
+
+ if (frame->base_scope && frame->base_scope->frame)
+ {
+ assert(frame->base_scope->frame == frame);
+ frame->scope->frame = ctx->call_ctx;
+
+ for(i = 0; i < frame->function->local_scopes[scope_index].locals_cnt; i++)
+ {
+ off = local_off(frame, frame->function->local_scopes[scope_index].locals[i].ref);
+ jsval_release(ctx->stack[off]);
+ ctx->stack[off] = jsval_undefined();
+ }
+ }
+ else
+ {
+ for(i = 0; i < frame->function->local_scopes[scope_index].locals_cnt; i++)
+ {
+ hres = jsdisp_propput_name(dispex, frame->function->local_scopes[scope_index].locals[i].name,
+ jsval_undefined());
+ if(FAILED(hres))
+ return hres;
+ }
+ }
+
+ return S_OK;
}
/* ECMA-262 3rd Edition 12.10 */
@@ -1036,7 +1109,7 @@ static HRESULT interp_enter_catch(script_ctx_t *ctx)
hres = jsdisp_propput_name(scope_obj, ident, v);
jsval_release(v);
if(SUCCEEDED(hres))
- hres = scope_push(ctx->call_ctx->scope, scope_obj, to_disp(scope_obj), &ctx->call_ctx->scope);
+ hres = scope_push(ctx->call_ctx->scope, scope_obj, to_disp(scope_obj), 0, &ctx->call_ctx->scope);
jsdisp_release(scope_obj);
return hres;
}
@@ -3032,7 +3105,7 @@ static HRESULT setup_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_t
frame->pop_variables = i;
- hres = scope_push(scope_chain, variable_object, to_disp(variable_object), &scope);
+ hres = scope_push(scope_chain, variable_object, to_disp(variable_object), 0, &scope);
if(FAILED(hres)) {
stack_popn(ctx, ctx->stack_top - orig_stack);
return hres;
diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h
index a94f1f211e4..3656b32dd8d 100644
--- a/dlls/jscript/engine.h
+++ b/dlls/jscript/engine.h
@@ -75,7 +75,7 @@
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_scope, 1, ARG_UINT, ARG_UINT) \
X(regexp, 1, ARG_STR, ARG_UINT) \
X(rshift, 1, 0,0) \
X(rshift2, 1, 0,0) \
@@ -180,7 +180,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 +222,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;
diff --git a/dlls/jscript/parser.h b/dlls/jscript/parser.h
index c4dd0752ee9..32bdc3b5186 100644
--- a/dlls/jscript/parser.h
+++ b/dlls/jscript/parser.h
@@ -129,6 +129,7 @@ struct _statement_t {
typedef struct {
statement_t stat;
+ unsigned int scope_index;
statement_t *stat_list;
} block_statement_t;
@@ -184,6 +185,7 @@ typedef struct {
statement_t stat;
expression_t *expr;
statement_t *statement;
+ unsigned int scope_index;
} with_statement_t;
typedef struct {
diff --git a/dlls/jscript/parser.y b/dlls/jscript/parser.y
index 7d0630582e0..86bfbe2fd01 100644
--- a/dlls/jscript/parser.y
+++ b/dlls/jscript/parser.y
@@ -1123,6 +1123,7 @@ static statement_t *new_block_statement(parser_ctx_t *ctx, unsigned loc, stateme
if(!ret)
return NULL;
+ ret->scope_index = 0;
ret->stat_list = list ? list->head : NULL;
return &ret->stat;
@@ -1306,6 +1307,7 @@ static statement_t *new_with_statement(parser_ctx_t *ctx, unsigned loc, expressi
ret->expr = expr;
ret->statement = statement;
+ ret->scope_index = 0;
return &ret->stat;
}
diff --git a/dlls/jscript/tests/lang.js b/dlls/jscript/tests/lang.js
index 2028d233ccf..baff4fbaa73 100644
--- a/dlls/jscript/tests/lang.js
+++ b/dlls/jscript/tests/lang.js
@@ -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;
}
diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js
index 24906de5231..4f590f9b8fd 100644
--- a/dlls/mshtml/tests/es5.js
+++ b/dlls/mshtml/tests/es5.js
@@ -1210,17 +1210,71 @@ 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();
+ }
+
+ if (0)
+ {
+ function func4()
+ {
+ }
}
+ todo_wine.ok(typeof func4 === 'undefined', "func4 is defined");
- todo_wine.ok(a == 3, "a != 3");
+ ok(a == 3, "a != 3");
});
--
2.31.1
More information about the wine-devel
mailing list