mirror of
git://source.winehq.org/git/wine.git
synced 2024-11-01 06:06:13 +00:00
jscript: Support nested scopes for functions defined inside.
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
308c8468b4
commit
e73cf60a74
6 changed files with 133 additions and 15 deletions
|
@ -41,6 +41,7 @@ typedef struct _statement_ctx_t {
|
|||
|
||||
unsigned int scope_index;
|
||||
BOOL block_scope;
|
||||
BOOL scope_has_functions;
|
||||
struct _statement_ctx_t *next;
|
||||
} statement_ctx_t;
|
||||
|
||||
|
@ -80,6 +81,7 @@ typedef struct _compiler_ctx_t {
|
|||
|
||||
function_expression_t *func_head;
|
||||
function_expression_t *func_tail;
|
||||
function_expression_t *current_function_expr;
|
||||
|
||||
heap_pool_t heap;
|
||||
} compiler_ctx_t;
|
||||
|
@ -979,6 +981,18 @@ static HRESULT compile_object_literal(compiler_ctx_t *ctx, property_value_expres
|
|||
|
||||
static HRESULT compile_function_expression(compiler_ctx_t *ctx, function_expression_t *expr, BOOL emit_ret)
|
||||
{
|
||||
statement_ctx_t *stat_ctx;
|
||||
|
||||
assert(ctx->current_function_expr);
|
||||
|
||||
for(stat_ctx = ctx->stat_ctx; stat_ctx; stat_ctx = stat_ctx->next)
|
||||
{
|
||||
if(stat_ctx->block_scope)
|
||||
break;
|
||||
}
|
||||
ctx->current_function_expr->scope_index = stat_ctx ? stat_ctx->scope_index : 0;
|
||||
ctx->current_function_expr = ctx->current_function_expr->next;
|
||||
|
||||
return emit_ret ? push_instr_uint(ctx, OP_func, expr->func_id) : S_OK;
|
||||
}
|
||||
|
||||
|
@ -1957,15 +1971,27 @@ static BOOL alloc_variable(compiler_ctx_t *ctx, const WCHAR *name, unsigned int
|
|||
|
||||
static HRESULT visit_function_expression(compiler_ctx_t *ctx, function_expression_t *expr)
|
||||
{
|
||||
statement_ctx_t *stat_ctx;
|
||||
|
||||
expr->func_id = ctx->func->func_cnt++;
|
||||
ctx->func_tail = ctx->func_tail ? (ctx->func_tail->next = expr) : (ctx->func_head = expr);
|
||||
|
||||
if(!expr->identifier || expr->event_target)
|
||||
return S_OK;
|
||||
|
||||
for (stat_ctx = ctx->stat_ctx; stat_ctx; stat_ctx = stat_ctx->next)
|
||||
{
|
||||
if (stat_ctx->block_scope)
|
||||
{
|
||||
stat_ctx->scope_has_functions = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!expr->is_statement && ctx->parser->script->version >= SCRIPTLANGUAGEVERSION_ES5)
|
||||
return S_OK;
|
||||
|
||||
return alloc_variable(ctx, expr->identifier, 0) ? S_OK : E_OUTOFMEMORY;
|
||||
return alloc_variable(ctx, expr->identifier, stat_ctx ? stat_ctx->scope_index : 0) ? S_OK : E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
static HRESULT visit_expression(compiler_ctx_t *ctx, expression_t *expr)
|
||||
|
@ -2150,7 +2176,7 @@ static HRESULT visit_block_statement(compiler_ctx_t *ctx, block_statement_t *blo
|
|||
iter = iter->next;
|
||||
}
|
||||
|
||||
if (needs_scope && !ctx->local_scopes[stat_ctx.scope_index].locals_cnt)
|
||||
if (needs_scope && !(ctx->local_scopes[stat_ctx.scope_index].locals_cnt || stat_ctx.scope_has_functions))
|
||||
remove_local_scope(ctx, block->scope_index);
|
||||
|
||||
return S_OK;
|
||||
|
@ -2434,6 +2460,7 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source,
|
|||
|
||||
func->bytecode = ctx->code;
|
||||
func->local_ref = INVALID_LOCAL_REF;
|
||||
func->scope_index = 0;
|
||||
ctx->func_head = ctx->func_tail = NULL;
|
||||
ctx->from_eval = from_eval;
|
||||
ctx->func = func;
|
||||
|
@ -2518,6 +2545,7 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source,
|
|||
return E_OUTOFMEMORY;
|
||||
memset(func->funcs, 0, func->func_cnt * sizeof(*func->funcs));
|
||||
|
||||
ctx->current_function_expr = ctx->func_head;
|
||||
off = ctx->code_off;
|
||||
hres = compile_block_statement(ctx, NULL, source->statement);
|
||||
if(FAILED(hres))
|
||||
|
@ -2539,10 +2567,13 @@ static HRESULT compile_function(compiler_ctx_t *ctx, source_elements_t *source,
|
|||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
TRACE("[%d] func %s\n", i, debugstr_w(func->funcs[i].name));
|
||||
func->funcs[i].scope_index = iter->scope_index;
|
||||
|
||||
TRACE("[%d] func %s, scope_index %u\n", i, debugstr_w(func->funcs[i].name), iter->scope_index);
|
||||
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, 0);
|
||||
local_ref_t *local_ref = lookup_local(func, func->funcs[i].name, func->funcs[i].scope_index);
|
||||
|
||||
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)
|
||||
|
|
|
@ -599,6 +599,9 @@ static HRESULT detach_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_
|
|||
|
||||
if (FAILED(hres = jsdisp_propput_name(scope->jsobj, name, ctx->stack[local_off(frame, ref)])))
|
||||
return hres;
|
||||
if (frame->function->variables[ref].func_id != -1 && scope != frame->base_scope
|
||||
&& FAILED(hres = jsdisp_propput_name(frame->variable_obj, name, ctx->stack[local_off(frame, ref)])))
|
||||
return hres;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -607,15 +610,9 @@ static HRESULT detach_scope_chain(script_ctx_t *ctx, call_frame_t *frame, scope_
|
|||
{
|
||||
HRESULT hres;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if ((hres = detach_scope(ctx, frame, scope)))
|
||||
if (scope != frame->base_scope && FAILED(hres = detach_scope_chain(ctx, frame, scope->next)))
|
||||
return hres;
|
||||
if (scope == frame->base_scope)
|
||||
break;
|
||||
scope = scope->next;
|
||||
}
|
||||
return S_OK;
|
||||
return detach_scope(ctx, frame, scope);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -910,7 +907,24 @@ static HRESULT scope_init_locals(script_ctx_t *ctx)
|
|||
{
|
||||
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();
|
||||
jsdisp_t *func_obj;
|
||||
jsval_t val;
|
||||
|
||||
if (frame->function->variables[ref].func_id != -1)
|
||||
{
|
||||
TRACE("function %s %d\n", debugstr_w(name), i);
|
||||
|
||||
if (FAILED(hres = create_source_function(ctx, frame->bytecode, frame->function->funcs
|
||||
+ frame->function->variables[ref].func_id, ctx->call_ctx->scope, &func_obj)))
|
||||
return hres;
|
||||
val = jsval_obj(func_obj);
|
||||
if (detached_vars && FAILED(hres = jsdisp_propput_name(frame->variable_obj, name, jsval_obj(func_obj))))
|
||||
return hres;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = jsval_undefined();
|
||||
}
|
||||
|
||||
if (detached_vars)
|
||||
{
|
||||
|
@ -971,6 +985,12 @@ static HRESULT interp_pop_scope(script_ctx_t *ctx)
|
|||
{
|
||||
TRACE("\n");
|
||||
|
||||
if(ctx->call_ctx->scope->ref > 1) {
|
||||
HRESULT hres = detach_variable_object(ctx, ctx->call_ctx, FALSE);
|
||||
if(FAILED(hres))
|
||||
ERR("Failed to detach variable object: %08x\n", hres);
|
||||
}
|
||||
|
||||
scope_pop(&ctx->call_ctx->scope);
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -3165,7 +3185,9 @@ static HRESULT setup_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_t
|
|||
}
|
||||
|
||||
for(i = 0; i < frame->function->func_cnt; i++) {
|
||||
if(frame->function->funcs[i].local_ref != INVALID_LOCAL_REF) {
|
||||
if(frame->function->funcs[i].local_ref != INVALID_LOCAL_REF
|
||||
&& !frame->function->funcs[i].scope_index)
|
||||
{
|
||||
jsdisp_t *func_obj;
|
||||
unsigned off;
|
||||
|
||||
|
@ -3219,6 +3241,12 @@ HRESULT exec_source(script_ctx_t *ctx, DWORD flags, bytecode_t *bytecode, functi
|
|||
if(!function->funcs[i].event_target)
|
||||
continue;
|
||||
|
||||
if (function->funcs[i].scope_index)
|
||||
{
|
||||
/* TODO: Add tests and handle in interp_push_scope(). */
|
||||
FIXME("Event target with scope index are not properly handled.\n");
|
||||
}
|
||||
|
||||
hres = create_source_function(ctx, bytecode, function->funcs+i, scope, &func_obj);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
@ -3249,6 +3277,12 @@ HRESULT exec_source(script_ctx_t *ctx, DWORD flags, bytecode_t *bytecode, functi
|
|||
if(function->variables[i].func_id != -1) {
|
||||
jsdisp_t *func_obj;
|
||||
|
||||
if (function->funcs[function->variables[i].func_id].scope_index && flags & EXEC_EVAL)
|
||||
{
|
||||
/* TODO: Add tests and handle in interp_push_scope(). */
|
||||
FIXME("Functions with scope index inside eval() are not properly handled.\n");
|
||||
}
|
||||
|
||||
hres = create_source_function(ctx, bytecode, function->funcs+function->variables[i].func_id, scope, &func_obj);
|
||||
if(FAILED(hres))
|
||||
goto fail;
|
||||
|
|
|
@ -177,6 +177,8 @@ typedef struct _function_code_t {
|
|||
local_ref_scopes_t *local_scopes;
|
||||
unsigned local_scope_count;
|
||||
|
||||
unsigned int scope_index; /* index of scope in the parent function where the function is defined */
|
||||
|
||||
bytecode_t *bytecode;
|
||||
} function_code_t;
|
||||
|
||||
|
|
|
@ -305,6 +305,7 @@ typedef struct _function_expression_t {
|
|||
DWORD src_len;
|
||||
unsigned func_id;
|
||||
BOOL is_statement;
|
||||
unsigned int scope_index;
|
||||
|
||||
struct _function_expression_t *next; /* for compiler */
|
||||
} function_expression_t;
|
||||
|
|
|
@ -1415,6 +1415,7 @@ static expression_t *new_function_expression(parser_ctx_t *ctx, const WCHAR *ide
|
|||
ret->src_str = src_str;
|
||||
ret->src_len = src_len;
|
||||
ret->is_statement = FALSE;
|
||||
ret->scope_index = 0;
|
||||
ret->next = NULL;
|
||||
|
||||
return &ret->expr;
|
||||
|
|
|
@ -1266,6 +1266,55 @@ sync_test("declaration_let", function() {
|
|||
ok(b == 1, "func2: b != 1");
|
||||
}
|
||||
func2();
|
||||
|
||||
var w = 8;
|
||||
with({w: 9})
|
||||
{
|
||||
{
|
||||
let c = 5
|
||||
|
||||
function func3(b, expected)
|
||||
{
|
||||
var b = 2
|
||||
|
||||
ok(typeof d === 'undefined', "d is defined");
|
||||
|
||||
ok(c == expected, "func3: c != expected");
|
||||
ok(w == 9, "w != 9")
|
||||
ok(b == 2, "func3: b != 2");
|
||||
b = 3;
|
||||
ok(b == 3, "func3: b != 3");
|
||||
ok(a == expected, "func3: a != expected");
|
||||
a = 6;
|
||||
c = 6;
|
||||
}
|
||||
|
||||
let f3 = func3
|
||||
let f4 = function()
|
||||
{
|
||||
ok(a == 6, "f4: a != 6");
|
||||
}
|
||||
|
||||
ok(a == 5, "tmp 2 a != 5");
|
||||
ok(c == 5, "c != 5");
|
||||
func3(1, 5)
|
||||
ok(c == 6, "c != 6");
|
||||
call_func(func3, 6);
|
||||
f3(1, 6)
|
||||
ok(a == 6, "a != 6");
|
||||
ok(b == 4, "b != 4");
|
||||
ok(c == 6, "c != 6");
|
||||
|
||||
call_func(f4);
|
||||
f4();
|
||||
}
|
||||
}
|
||||
{
|
||||
let c = 4;
|
||||
let d = 1;
|
||||
|
||||
func3(1, 6);
|
||||
}
|
||||
}
|
||||
|
||||
ok(a == 3, "a != 3");
|
||||
|
|
Loading…
Reference in a new issue