[PATCH v4 3/6] jscript: Support block scope variables.

Paul Gofman pgofman at codeweavers.com
Thu Jun 17 16:25:20 CDT 2021


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
v4:
    - remove a TRACE in bind_local();
    - do not create block scope variables object for each block with locals;
    - introcude distinct opcodes for with and block scope pushes (and introduce
      a separate scope_init_locals() function to use that also from interp_push_with_scope()
      in the next patch;
    - fix local refs handling for detached vars in stack_topn_exprval():
        - don't search for function parameters in the block and with scopes;
        - do not omit base_scope in search;
    - refactor detach_scope_chain() changes for better code structure and saner diff in the
      next patch.


 dlls/jscript/compile.c     | 106 ++++++++++++--------
 dlls/jscript/engine.c      | 192 ++++++++++++++++++++++++++++++-------
 dlls/jscript/engine.h      |   6 +-
 dlls/jscript/tests/lang.js |  16 ++++
 dlls/mshtml/tests/es5.js   |  48 +++++++++-
 5 files changed, 291 insertions(+), 77 deletions(-)

diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c
index 4eac4bc884a..8003725e35a 100644
--- a/dlls/jscript/compile.c
+++ b/dlls/jscript/compile.c
@@ -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,8 +1623,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(ctx, OP_push_with_scope, stat->scope_index)))
+        return hres;
 
     hres = compile_statement(ctx, &stat_ctx, stat->statement);
     if(FAILED(hres))
@@ -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)
-{
-    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)
+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;
 }
 
@@ -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);
@@ -2425,8 +2446,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;
@@ -2465,7 +2484,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;
     }
 
@@ -2473,30 +2492,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)
@@ -2504,7 +2528,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;
 
@@ -2527,7 +2551,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)
diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c
index c6ca3a9c10d..b89472c2cc1 100644
--- a/dlls/jscript/engine.c
+++ b/dlls/jscript/engine.c
@@ -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;
         }
@@ -391,7 +406,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;
 
@@ -401,11 +417,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 = scope_index;
 
     *ret = new_scope;
     return S_OK;
@@ -428,7 +446,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 +572,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 +641,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 +685,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 +705,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 +728,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,14 +882,61 @@ 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)
 {
+    unsigned int scope_index = get_op_uint(ctx, 0);
     IDispatch *disp;
     jsval_t v;
     HRESULT hres;
 
-    TRACE("\n");
+    TRACE("scope_index %u.\n", scope_index);
 
     v = stack_pop(ctx);
     hres = to_object(ctx, v, &disp);
@@ -835,11 +944,28 @@ 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);
+    hres = scope_push(ctx->call_ctx->scope, to_jsdisp(disp), disp, scope_index, &ctx->call_ctx->scope);
     IDispatch_Release(disp);
     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, scope_index, &frame->scope);
+
+    if (FAILED(hres) || !scope_index)
+        return hres;
+
+    return scope_init_locals(ctx);
+}
+
 /* ECMA-262 3rd Edition    12.10 */
 static HRESULT interp_pop_scope(script_ctx_t *ctx)
 {
@@ -1036,7 +1162,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 +3158,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..65ccba7eaa2 100644
--- a/dlls/jscript/engine.h
+++ b/dlls/jscript/engine.h
@@ -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, ARG_UINT, 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;
diff --git a/dlls/jscript/tests/lang.js b/dlls/jscript/tests/lang.js
index 0db86c89985..ad1217d6b60 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..f4710348739 100644
--- a/dlls/mshtml/tests/es5.js
+++ b/dlls/mshtml/tests/es5.js
@@ -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");
 });
-- 
2.31.1




More information about the wine-devel mailing list