Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
The current code is flawed and will not pass the tests at the end. This will be needed for the last patch, it is also the same behavior as wine's vbscript.
dlls/jscript/jscript.c | 98 +++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 50 deletions(-)
diff --git a/dlls/jscript/jscript.c b/dlls/jscript/jscript.c index 8f97fa8..8fc39c2 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -57,6 +57,7 @@ typedef struct { DWORD version; BOOL html_mode; BOOL is_encode; + BOOL is_initialized;
IActiveScriptSite *site;
@@ -343,23 +344,6 @@ static void exec_queued_code(JScript *This) clear_script_queue(This); }
-static HRESULT set_ctx_site(JScript *This) -{ - HRESULT hres; - - This->ctx->lcid = This->lcid; - - hres = init_global(This->ctx); - if(FAILED(hres)) - return hres; - - IActiveScriptSite_AddRef(This->site); - This->ctx->site = This->site; - - change_state(This, SCRIPTSTATE_INITIALIZED); - return S_OK; -} - static void decrease_state(JScript *This, SCRIPTSTATE state) { if(This->ctx) { @@ -641,6 +625,35 @@ static HRESULT WINAPI JScript_SetScriptSite(IActiveScript *iface, if(InterlockedCompareExchange(&This->thread_id, GetCurrentThreadId(), 0)) return E_UNEXPECTED;
+ if(!This->ctx) { + script_ctx_t *ctx = heap_alloc_zero(sizeof(script_ctx_t)); + if(!ctx) + return E_OUTOFMEMORY; + + ctx->ref = 1; + ctx->state = SCRIPTSTATE_UNINITIALIZED; + ctx->active_script = &This->IActiveScript_iface; + ctx->safeopt = This->safeopt; + ctx->version = This->version; + ctx->html_mode = This->html_mode; + ctx->acc = jsval_undefined(); + heap_pool_init(&ctx->tmp_heap); + + hres = create_jscaller(ctx); + if(FAILED(hres)) { + heap_free(ctx); + return hres; + } + + ctx->last_match = jsstr_empty(); + + ctx = InterlockedCompareExchangePointer((void**)&This->ctx, ctx, NULL); + if(ctx) { + script_release(ctx); + return E_UNEXPECTED; + } + } + This->site = pass; IActiveScriptSite_AddRef(This->site);
@@ -648,7 +661,18 @@ static HRESULT WINAPI JScript_SetScriptSite(IActiveScript *iface, if(hres == S_OK) This->lcid = lcid;
- return This->ctx ? set_ctx_site(This) : S_OK; + This->ctx->lcid = This->lcid; + + hres = init_global(This->ctx); + if(FAILED(hres)) + return hres; + + IActiveScriptSite_AddRef(This->site); + This->ctx->site = This->site; + + if(This->is_initialized) + change_state(This, SCRIPTSTATE_INITIALIZED); + return S_OK; }
static HRESULT WINAPI JScript_GetScriptSite(IActiveScript *iface, REFIID riid, @@ -677,7 +701,7 @@ static HRESULT WINAPI JScript_SetScriptState(IActiveScript *iface, SCRIPTSTATE s return S_OK; }
- if(!This->ctx) + if(!This->is_initialized || !This->ctx) return E_UNEXPECTED;
switch(ss) { @@ -900,42 +924,16 @@ static ULONG WINAPI JScriptParse_Release(IActiveScriptParse *iface) static HRESULT WINAPI JScriptParse_InitNew(IActiveScriptParse *iface) { JScript *This = impl_from_IActiveScriptParse(iface); - script_ctx_t *ctx; - HRESULT hres;
TRACE("(%p)\n", This);
- if(This->ctx) + if(This->is_initialized) return E_UNEXPECTED; + This->is_initialized = TRUE;
- ctx = heap_alloc_zero(sizeof(script_ctx_t)); - if(!ctx) - return E_OUTOFMEMORY; - - ctx->ref = 1; - ctx->state = SCRIPTSTATE_UNINITIALIZED; - ctx->active_script = &This->IActiveScript_iface; - ctx->safeopt = This->safeopt; - ctx->version = This->version; - ctx->html_mode = This->html_mode; - ctx->acc = jsval_undefined(); - heap_pool_init(&ctx->tmp_heap); - - hres = create_jscaller(ctx); - if(FAILED(hres)) { - heap_free(ctx); - return hres; - } - - ctx->last_match = jsstr_empty(); - - ctx = InterlockedCompareExchangePointer((void**)&This->ctx, ctx, NULL); - if(ctx) { - script_release(ctx); - return E_UNEXPECTED; - } - - return This->site ? set_ctx_site(This) : S_OK; + if(This->site) + change_state(This, SCRIPTSTATE_INITIALIZED); + return S_OK; }
static HRESULT WINAPI JScriptParse_AddScriptlet(IActiveScriptParse *iface,
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Not only is this more correct, but it also simplifies the 7th patch. Clearing it in SCRIPTSTATE_UNINITIALIZED is now redundant.
dlls/jscript/jscript.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dlls/jscript/jscript.c b/dlls/jscript/jscript.c index 8fc39c2..98fe759 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -363,6 +363,8 @@ static void decrease_state(JScript *This, SCRIPTSTATE state) return; /* FALLTHROUGH */ case SCRIPTSTATE_INITIALIZED: + clear_script_queue(This); + if(This->ctx->host_global) { IDispatch_Release(This->ctx->host_global); This->ctx->host_global = NULL; @@ -402,7 +404,6 @@ static void decrease_state(JScript *This, SCRIPTSTATE state) /* FALLTHROUGH */ case SCRIPTSTATE_UNINITIALIZED: change_state(This, state); - clear_script_queue(This); break; default: assert(0);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/jscript.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 8db76bb..113ec99 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -204,6 +204,14 @@ typedef HRESULT (*builtin_setter_t)(script_ctx_t*,jsdisp_t*,jsval_t);
HRESULT builtin_set_const(script_ctx_t*,jsdisp_t*,jsval_t) DECLSPEC_HIDDEN;
+typedef struct named_item_t { + IDispatch *disp; + DWORD flags; + LPWSTR name; + + struct named_item_t *next; +} named_item_t; + typedef struct { const WCHAR *name; builtin_invoke_t invoke; @@ -356,14 +364,6 @@ static inline BOOL is_digit(WCHAR c) return '0' <= c && c <= '9'; }
-typedef struct named_item_t { - IDispatch *disp; - DWORD flags; - LPWSTR name; - - struct named_item_t *next; -} named_item_t; - typedef struct _cc_var_t cc_var_t;
typedef struct {
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This will simplify the 7th patch.
dlls/jscript/engine.c | 34 ++++++---------------------------- dlls/jscript/jscript.c | 35 +++++++++++++++++++++++++++++++++++ dlls/jscript/jscript.h | 2 ++ 3 files changed, 43 insertions(+), 28 deletions(-)
diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index f94cfd1..4e28b63 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -662,34 +662,12 @@ static HRESULT identifier_eval(script_ctx_t *ctx, BSTR identifier, exprval_t *re return S_OK; }
- for(item = ctx->named_items; item; item = item->next) { - if((item->flags & SCRIPTITEM_ISVISIBLE) && !wcscmp(item->name, identifier)) { - if(!item->disp) { - IUnknown *unk; - - if(!ctx->site) - break; - - hres = IActiveScriptSite_GetItemInfo(ctx->site, identifier, - SCRIPTINFO_IUNKNOWN, &unk, NULL); - if(FAILED(hres)) { - WARN("GetItemInfo failed: %08x\n", hres); - break; - } - - hres = IUnknown_QueryInterface(unk, &IID_IDispatch, (void**)&item->disp); - IUnknown_Release(unk); - if(FAILED(hres)) { - WARN("object does not implement IDispatch\n"); - break; - } - } - - IDispatch_AddRef(item->disp); - ret->type = EXPRVAL_JSVAL; - ret->u.val = jsval_disp(item->disp); - return S_OK; - } + item = lookup_named_item(ctx, identifier, SCRIPTITEM_ISVISIBLE); + if(item) { + IDispatch_AddRef(item->disp); + ret->type = EXPRVAL_JSVAL; + ret->u.val = jsval_disp(item->disp); + return S_OK; }
if(lookup_global_members(ctx, identifier, ret)) diff --git a/dlls/jscript/jscript.c b/dlls/jscript/jscript.c index 98fe759..1970437 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -108,6 +108,41 @@ static inline BOOL is_started(script_ctx_t *ctx) || ctx->state == SCRIPTSTATE_DISCONNECTED; }
+named_item_t *lookup_named_item(script_ctx_t *ctx, const WCHAR *item_name, unsigned flags) +{ + named_item_t *item; + HRESULT hr; + + for(item = ctx->named_items; item; item = item->next) { + if((item->flags & flags) == flags && !wcscmp(item->name, item_name)) { + if(!item->disp) { + IUnknown *unk; + + if(!ctx->site) + return NULL; + + hr = IActiveScriptSite_GetItemInfo(ctx->site, item_name, + SCRIPTINFO_IUNKNOWN, &unk, NULL); + if(FAILED(hr)) { + WARN("GetItemInfo failed: %08x\n", hr); + continue; + } + + hr = IUnknown_QueryInterface(unk, &IID_IDispatch, (void**)&item->disp); + IUnknown_Release(unk); + if(FAILED(hr)) { + WARN("object does not implement IDispatch\n"); + continue; + } + } + + return item; + } + } + + return NULL; +} + static inline JScriptError *impl_from_IActiveScriptError(IActiveScriptError *iface) { return CONTAINING_RECORD(iface, JScriptError, IActiveScriptError_iface); diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 113ec99..8297838 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -212,6 +212,8 @@ typedef struct named_item_t { struct named_item_t *next; } named_item_t;
+named_item_t *lookup_named_item(script_ctx_t*,const WCHAR*,unsigned) DECLSPEC_HIDDEN; + typedef struct { const WCHAR *name; builtin_invoke_t invoke;
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
There are no existing tests for named items for jscript. So they will be added with the last patch. It's the same as vbscript, anyway.
dlls/jscript/jscript.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/jscript/jscript.c b/dlls/jscript/jscript.c index 1970437..f97c36f 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -115,7 +115,7 @@ named_item_t *lookup_named_item(script_ctx_t *ctx, const WCHAR *item_name, unsig
for(item = ctx->named_items; item; item = item->next) { if((item->flags & flags) == flags && !wcscmp(item->name, item_name)) { - if(!item->disp) { + if(!item->disp && (flags || !(item->flags & SCRIPTITEM_CODEONLY))) { IUnknown *unk;
if(!ctx->site)
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Needed to pass the tests at the end of the series. Otherwise, OnLeaveScript is not called when an error occurs.
dlls/jscript/jscript.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/jscript/jscript.c b/dlls/jscript/jscript.c index f97c36f..3ec259f 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -320,12 +320,14 @@ HRESULT leave_script(script_ctx_t *ctx, HRESULT result) if(FAILED(result)) { WARN("%08x\n", result); if(ctx->site && (error = heap_alloc(sizeof(*error)))) { + BOOL enter_notified = ei->enter_notified; HRESULT hres;
error->IActiveScriptError_iface.lpVtbl = &JScriptErrorVtbl; error->ref = 1; error->ei = *ei; memset(ei, 0, sizeof(*ei)); + ei->enter_notified = enter_notified;
hres = IActiveScriptSite_OnScriptError(ctx->site, &error->IActiveScriptError_iface); IActiveScriptError_Release(&error->IActiveScriptError_iface);
Each named item should have its own associated script dispatch object. Identifiers are added to this object, but code does look into the global script object as well.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/compile.c | 2 + dlls/jscript/dispex.c | 3 +- dlls/jscript/engine.c | 19 +++++- dlls/jscript/engine.h | 5 +- dlls/jscript/function.c | 6 ++ dlls/jscript/global.c | 4 +- dlls/jscript/jscript.c | 127 ++++++++++++++++++++++++++++++++++++---- dlls/jscript/jscript.h | 5 ++ dlls/jscript/jsutils.c | 4 +- 9 files changed, 157 insertions(+), 18 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index 61db10c..07027c2 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -2250,6 +2250,8 @@ void release_bytecode(bytecode_t *code) for(i=0; i < code->str_cnt; i++) jsstr_release(code->str_pool[i]);
+ if(code->named_item) + release_named_item(code->named_item); heap_free(code->source); heap_pool_free(&code->heap); heap_free(code->bstr_pool); diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index e986de6..6dd4a3a 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -1526,7 +1526,8 @@ static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lc return DISP_E_MEMBERNOTFOUND; }
- enter_script(This->ctx, &ei); + hres = enter_script(This->ctx, This->named_item, &ei); + if(FAILED(hres)) return hres;
switch(wFlags) { case DISPATCH_METHOD|DISPATCH_PROPERTYGET: diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index 4e28b63..79b0c57 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -656,6 +656,14 @@ static HRESULT identifier_eval(script_ctx_t *ctx, BSTR identifier, exprval_t *re } }
+ if(ctx->item_context) { + hres = jsdisp_get_id(ctx->item_context, identifier, 0, &id); + if(SUCCEEDED(hres)) { + exprval_set_disp_ref(ret, to_disp(ctx->item_context), id); + return S_OK; + } + } + hres = jsdisp_get_id(ctx->global, identifier, 0, &id); if(SUCCEEDED(hres)) { exprval_set_disp_ref(ret, to_disp(ctx->global), id); @@ -1236,13 +1244,14 @@ static HRESULT interp_identifier_ref(script_ctx_t *ctx, BSTR identifier, unsigne return hres;
if(exprval.type == EXPRVAL_INVALID && (flags & fdexNameEnsure)) { + jsdisp_t *jsdisp = ctx->item_context ? ctx->item_context : ctx->global; DISPID id;
- hres = jsdisp_get_id(ctx->global, identifier, fdexNameEnsure, &id); + hres = jsdisp_get_id(jsdisp, identifier, fdexNameEnsure, &id); if(FAILED(hres)) return hres;
- exprval_set_disp_ref(&exprval, to_disp(ctx->global), id); + exprval_set_disp_ref(&exprval, to_disp(jsdisp), id); }
if(exprval.type == EXPRVAL_JSVAL || exprval.type == EXPRVAL_INVALID) { @@ -2994,6 +3003,8 @@ HRESULT exec_source(script_ctx_t *ctx, DWORD flags, bytecode_t *bytecode, functi }
if(flags & (EXEC_GLOBAL | EXEC_EVAL)) { + BOOL lookup_globals = (flags & EXEC_GLOBAL) && !ctx->item_context; + 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) { @@ -3005,7 +3016,7 @@ HRESULT exec_source(script_ctx_t *ctx, DWORD flags, bytecode_t *bytecode, functi
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)) { + }else if(!lookup_globals || !lookup_global_members(ctx, function->variables[i].name, NULL)) { DISPID id = 0;
hres = jsdisp_get_id(variable_obj, function->variables[i].name, fdexNameEnsure, &id); @@ -3057,6 +3068,8 @@ HRESULT exec_source(script_ctx_t *ctx, DWORD flags, bytecode_t *bytecode, functi frame->stack_base = ctx->stack_top; if(this_obj) frame->this_obj = this_obj; + else if(ctx->item_context) + frame->this_obj = to_disp(ctx->item_context); else if(ctx->host_global) frame->this_obj = ctx->host_global; else diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index cad46e1..d50b0fa 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -180,6 +180,7 @@ struct _bytecode_t { heap_pool_t heap;
function_code_t global_code; + named_item_t *named_item;
WCHAR *source; UINT64 source_context; @@ -231,6 +232,8 @@ struct _jsexcept_t { jsstr_t *message; jsstr_t *line;
+ jsdisp_t *item_context; + bytecode_t *code; unsigned loc;
@@ -238,7 +241,7 @@ struct _jsexcept_t { jsexcept_t *prev; };
-void enter_script(script_ctx_t*,jsexcept_t*) DECLSPEC_HIDDEN; +HRESULT enter_script(script_ctx_t*,named_item_t*,jsexcept_t*) DECLSPEC_HIDDEN; HRESULT leave_script(script_ctx_t*,HRESULT) DECLSPEC_HIDDEN; void reset_ei(jsexcept_t*) DECLSPEC_HIDDEN; void set_error_location(jsexcept_t*,bytecode_t*,unsigned,unsigned,jsstr_t*) DECLSPEC_HIDDEN; diff --git a/dlls/jscript/function.c b/dlls/jscript/function.c index baa5eb1..f34745d 100644 --- a/dlls/jscript/function.c +++ b/dlls/jscript/function.c @@ -612,6 +612,8 @@ static HRESULT NativeFunction_call(script_ctx_t *ctx, FunctionInstance *func, ID
if(this_disp) set_disp(&vthis, this_disp); + else if(ctx->item_context) + set_jsdisp(&vthis, ctx->item_context); else if(ctx->host_global) set_disp(&vthis, ctx->host_global); else @@ -820,6 +822,10 @@ HRESULT create_source_function(script_ctx_t *ctx, bytecode_t *code, function_cod }
bytecode_addref(code); + if(!code->named_item && ctx->item_context) { + code->named_item = ctx->item_context->named_item; + code->named_item->ref++; + } function->code = code; function->func_code = func_code; function->function.length = function->func_code->param_cnt; diff --git a/dlls/jscript/global.c b/dlls/jscript/global.c index e2758bf..3bf06de 100644 --- a/dlls/jscript/global.c +++ b/dlls/jscript/global.c @@ -181,6 +181,7 @@ HRESULT JSGlobal_eval(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned a { call_frame_t *frame = ctx->call_ctx; DWORD exec_flags = EXEC_EVAL; + jsdisp_t *context; bytecode_t *code; const WCHAR *src; HRESULT hres; @@ -210,12 +211,13 @@ HRESULT JSGlobal_eval(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned a return hres; }
+ context = frame ? frame->variable_obj : (ctx->item_context ? ctx->item_context : ctx->global); if(!frame || (frame->flags & EXEC_GLOBAL)) exec_flags |= EXEC_GLOBAL; if(flags & DISPATCH_JSCRIPT_CALLEREXECSSOURCE) exec_flags |= EXEC_RETURN_TO_INTERP; hres = exec_source(ctx, exec_flags, code, &code->global_code, frame ? frame->scope : NULL, - frame ? frame->this_obj : NULL, NULL, frame ? frame->variable_obj : ctx->global, 0, NULL, r); + frame ? frame->this_obj : NULL, NULL, context, 0, NULL, r); release_bytecode(code); return hres; } diff --git a/dlls/jscript/jscript.c b/dlls/jscript/jscript.c index 3ec259f..e562f2d 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -108,6 +108,32 @@ static inline BOOL is_started(script_ctx_t *ctx) || ctx->state == SCRIPTSTATE_DISCONNECTED; }
+static HRESULT create_named_item_script_obj(script_ctx_t *ctx, named_item_t *item) +{ + static const builtin_info_t disp_info = { + JSCLASS_GLOBAL, + {NULL, NULL, 0}, + 0, NULL, + NULL, + NULL + }; + HRESULT hr = create_dispex(ctx, &disp_info, NULL, &item->script_obj); + + if(FAILED(hr)) + return hr; + item->script_obj->named_item = item; + return S_OK; +} + +static void release_named_item_script_obj(named_item_t *item) +{ + if(!item->script_obj) return; + + item->script_obj->named_item = NULL; + jsdisp_release(item->script_obj); + item->script_obj = NULL; +} + named_item_t *lookup_named_item(script_ctx_t *ctx, const WCHAR *item_name, unsigned flags) { named_item_t *item; @@ -115,6 +141,10 @@ named_item_t *lookup_named_item(script_ctx_t *ctx, const WCHAR *item_name, unsig
for(item = ctx->named_items; item; item = item->next) { if((item->flags & flags) == flags && !wcscmp(item->name, item_name)) { + if(!item->script_obj) { + hr = create_named_item_script_obj(ctx, item); + if(FAILED(hr)) return NULL; + } if(!item->disp && (flags || !(item->flags & SCRIPTITEM_CODEONLY))) { IUnknown *unk;
@@ -143,6 +173,14 @@ named_item_t *lookup_named_item(script_ctx_t *ctx, const WCHAR *item_name, unsig return NULL; }
+void release_named_item(named_item_t *item) +{ + if(--item->ref) return; + + heap_free(item->name); + heap_free(item); +} + static inline JScriptError *impl_from_IActiveScriptError(IActiveScriptError *iface) { return CONTAINING_RECORD(iface, JScriptError, IActiveScriptError_iface); @@ -295,12 +333,24 @@ void reset_ei(jsexcept_t *ei) } }
-void enter_script(script_ctx_t *ctx, jsexcept_t *ei) +HRESULT enter_script(script_ctx_t *ctx, named_item_t *item, jsexcept_t *ei) { + HRESULT hres; + memset(ei, 0, sizeof(*ei)); + if(item) { + if(!item->script_obj) { + hres = create_named_item_script_obj(ctx, item); + if(FAILED(hres)) return hres; + } + ei->item_context = item->script_obj; + } + ctx->item_context = ei->item_context; + ei->prev = ctx->ei; ctx->ei = ei; TRACE("ctx %p ei %p prev %p\n", ctx, ei, ei->prev); + return S_OK; }
HRESULT leave_script(script_ctx_t *ctx, HRESULT result) @@ -311,6 +361,7 @@ HRESULT leave_script(script_ctx_t *ctx, HRESULT result) TRACE("ctx %p ei %p prev %p\n", ctx, ei, ei->prev);
ctx->ei = ei->prev; + ctx->item_context = ctx->ei ? ctx->ei->item_context : NULL; if(result == DISP_E_EXCEPTION) { result = ei->error; }else { @@ -364,6 +415,15 @@ static void clear_persistent_code_list(JScript *This) } }
+static void release_persistent_script_objs(JScript *This) +{ + bytecode_t *iter; + + LIST_FOR_EACH_ENTRY(iter, &This->persistent_code, bytecode_t, entry) + if(iter->named_item) + release_named_item_script_obj(iter->named_item); +} + static void exec_queued_code(JScript *This) { bytecode_t *iter; @@ -371,8 +431,11 @@ static void exec_queued_code(JScript *This) HRESULT hres = S_OK;
LIST_FOR_EACH_ENTRY(iter, &This->queued_code, bytecode_t, entry) { - enter_script(This->ctx, &ei); - hres = exec_source(This->ctx, EXEC_GLOBAL, iter, &iter->global_code, NULL, NULL, NULL, This->ctx->global, 0, NULL, NULL); + hres = enter_script(This->ctx, iter->named_item, &ei); + if(FAILED(hres)) + break; + hres = exec_source(This->ctx, EXEC_GLOBAL, iter, &iter->global_code, NULL, NULL, NULL, + This->ctx->item_context ? This->ctx->item_context : This->ctx->global, 0, NULL, NULL); leave_script(This->ctx, hres); if(FAILED(hres)) break; @@ -401,6 +464,7 @@ static void decrease_state(JScript *This, SCRIPTSTATE state) /* FALLTHROUGH */ case SCRIPTSTATE_INITIALIZED: clear_script_queue(This); + release_persistent_script_objs(This);
if(This->ctx->host_global) { IDispatch_Release(This->ctx->host_global); @@ -416,8 +480,8 @@ static void decrease_state(JScript *This, SCRIPTSTATE state)
if(iter->disp) IDispatch_Release(iter->disp); - heap_free(iter->name); - heap_free(iter); + release_named_item_script_obj(iter); + release_named_item(iter); iter = iter2; }
@@ -834,8 +898,10 @@ static HRESULT WINAPI JScript_AddNamedItem(IActiveScript *iface, return E_OUTOFMEMORY; }
+ item->ref = 1; item->disp = disp; item->flags = dwFlags; + item->script_obj = NULL; item->name = heap_strdupW(pstrName); if(!item->name) { if(disp) @@ -862,6 +928,7 @@ static HRESULT WINAPI JScript_GetScriptDispatch(IActiveScript *iface, LPCOLESTR IDispatch **ppdisp) { JScript *This = impl_from_IActiveScript(iface); + jsdisp_t *script_obj;
TRACE("(%p)->(%s %p)\n", This, debugstr_w(pstrItemName), ppdisp);
@@ -873,7 +940,15 @@ static HRESULT WINAPI JScript_GetScriptDispatch(IActiveScript *iface, LPCOLESTR return E_UNEXPECTED; }
- *ppdisp = to_disp(This->ctx->global); + if(pstrItemName) { + named_item_t *item = lookup_named_item(This->ctx, pstrItemName, 0); + if(!item) return E_INVALIDARG; + script_obj = item->script_obj; + } + else + script_obj = This->ctx->global; + + *ppdisp = to_disp(script_obj); IDispatch_AddRef(*ppdisp); return S_OK; } @@ -994,6 +1069,7 @@ static HRESULT WINAPI JScriptParse_ParseScriptText(IActiveScriptParse *iface, DWORD dwFlags, VARIANT *pvarResult, EXCEPINFO *pexcepinfo) { JScript *This = impl_from_IActiveScriptParse(iface); + named_item_t *item = NULL; bytecode_t *code; jsexcept_t ei; HRESULT hres; @@ -1005,16 +1081,31 @@ static HRESULT WINAPI JScriptParse_ParseScriptText(IActiveScriptParse *iface, if(This->thread_id != GetCurrentThreadId() || This->ctx->state == SCRIPTSTATE_CLOSED) return E_UNEXPECTED;
- enter_script(This->ctx, &ei); + if(pstrItemName) { + item = lookup_named_item(This->ctx, pstrItemName, 0); + if(!item) { + WARN("Unknown context %s\n", debugstr_w(pstrItemName)); + return E_INVALIDARG; + } + } + + hres = enter_script(This->ctx, item, &ei); + if(FAILED(hres)) + return hres; hres = compile_script(This->ctx, pstrCode, dwSourceContextCookie, ulStartingLine, NULL, pstrDelimiter, (dwFlags & SCRIPTTEXT_ISEXPRESSION) != 0, This->is_encode, &code); if(FAILED(hres)) return leave_script(This->ctx, hres);
+ if(item) { + code->named_item = item; + item->ref++; + } if(dwFlags & SCRIPTTEXT_ISEXPRESSION) { jsval_t r;
- hres = exec_source(This->ctx, EXEC_GLOBAL, code, &code->global_code, NULL, NULL, NULL, This->ctx->global, 0, NULL, &r); + hres = exec_source(This->ctx, EXEC_GLOBAL, code, &code->global_code, NULL, NULL, NULL, + This->ctx->item_context ? This->ctx->item_context : This->ctx->global, 0, NULL, &r); if(SUCCEEDED(hres)) { if(pvarResult) hres = jsval_to_variant(r, pvarResult); @@ -1033,7 +1124,8 @@ static HRESULT WINAPI JScriptParse_ParseScriptText(IActiveScriptParse *iface, if(!pvarResult && !is_started(This->ctx)) { list_add_tail(&This->queued_code, &code->entry); }else { - hres = exec_source(This->ctx, EXEC_GLOBAL, code, &code->global_code, NULL, NULL, NULL, This->ctx->global, 0, NULL, NULL); + hres = exec_source(This->ctx, EXEC_GLOBAL, code, &code->global_code, NULL, NULL, NULL, + This->ctx->item_context ? This->ctx->item_context : This->ctx->global, 0, NULL, NULL); if(code->is_persistent) list_add_tail(&This->persistent_code, &code->entry); else @@ -1086,6 +1178,7 @@ static HRESULT WINAPI JScriptParseProcedure_ParseProcedureText(IActiveScriptPars CTXARG_T dwSourceContextCookie, ULONG ulStartingLineNumber, DWORD dwFlags, IDispatch **ppdisp) { JScript *This = impl_from_IActiveScriptParseProcedure2(iface); + named_item_t *item = NULL; bytecode_t *code; jsdisp_t *dispex; jsexcept_t ei; @@ -1098,7 +1191,17 @@ static HRESULT WINAPI JScriptParseProcedure_ParseProcedureText(IActiveScriptPars if(This->thread_id != GetCurrentThreadId() || This->ctx->state == SCRIPTSTATE_CLOSED) return E_UNEXPECTED;
- enter_script(This->ctx, &ei); + if(pstrItemName) { + item = lookup_named_item(This->ctx, pstrItemName, 0); + if(!item) { + WARN("Unknown context %s\n", debugstr_w(pstrItemName)); + return E_INVALIDARG; + } + } + + hres = enter_script(This->ctx, item, &ei); + if(FAILED(hres)) + return hres; hres = compile_script(This->ctx, pstrCode, dwSourceContextCookie, ulStartingLineNumber, pstrFormalParams, pstrDelimiter, FALSE, This->is_encode, &code); if(SUCCEEDED(hres)) @@ -1287,7 +1390,9 @@ static HRESULT WINAPI VariantChangeType_ChangeType(IVariantChangeType *iface, VA return E_UNEXPECTED; }
- enter_script(This->ctx, &ei); + hres = enter_script(This->ctx, NULL, &ei); + if(FAILED(hres)) + return hres; hres = variant_change_type(This->ctx, &res, src, vt); hres = leave_script(This->ctx, hres); if(FAILED(hres)) diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 8297838..9492f3f 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -205,7 +205,9 @@ typedef HRESULT (*builtin_setter_t)(script_ctx_t*,jsdisp_t*,jsval_t); HRESULT builtin_set_const(script_ctx_t*,jsdisp_t*,jsval_t) DECLSPEC_HIDDEN;
typedef struct named_item_t { + jsdisp_t *script_obj; IDispatch *disp; + unsigned ref; DWORD flags; LPWSTR name;
@@ -213,6 +215,7 @@ typedef struct named_item_t { } named_item_t;
named_item_t *lookup_named_item(script_ctx_t*,const WCHAR*,unsigned) DECLSPEC_HIDDEN; +void release_named_item(named_item_t*) DECLSPEC_HIDDEN;
typedef struct { const WCHAR *name; @@ -243,6 +246,7 @@ struct jsdisp_t { DWORD prop_cnt; dispex_prop_t *props; script_ctx_t *ctx; + named_item_t *named_item;
jsdisp_t *prototype;
@@ -433,6 +437,7 @@ struct _script_ctx_t { DWORD last_match_length;
jsdisp_t *global; + jsdisp_t *item_context; jsdisp_t *function_constr; jsdisp_t *array_constr; jsdisp_t *bool_constr; diff --git a/dlls/jscript/jsutils.c b/dlls/jscript/jsutils.c index 421116f..351255b 100644 --- a/dlls/jscript/jsutils.c +++ b/dlls/jscript/jsutils.c @@ -881,7 +881,9 @@ HRESULT variant_change_type(script_ctx_t *ctx, VARIANT *dst, VARIANT *src, VARTY if(FAILED(hres)) return hres;
- enter_script(ctx, &ei); + hres = enter_script(ctx, NULL, &ei); + if(FAILED(hres)) + return hres;
switch(vt) { case VT_I2:
Hi Gabriel,
First of all, this patch breaks mshtml script tests. It's likely that it's mshtml's fault, but we need to get it working.
On 19.02.2020 17:38, Gabriel Ivăncescu wrote:
-void enter_script(script_ctx_t *ctx, jsexcept_t *ei) +HRESULT enter_script(script_ctx_t *ctx, named_item_t *item, jsexcept_t *ei) {
- HRESULT hres;
memset(ei, 0, sizeof(*ei));
- if(item) {
if(!item->script_obj) {
hres = create_named_item_script_obj(ctx, item);
if(FAILED(hres)) return hres;
}
Can we have it in exec_source instead? If nothing else, it would simplify enter_script error handling.
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 8297838..9492f3f 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -205,7 +205,9 @@ typedef HRESULT (*builtin_setter_t)(script_ctx_t*,jsdisp_t*,jsval_t); HRESULT builtin_set_const(script_ctx_t*,jsdisp_t*,jsval_t) DECLSPEC_HIDDEN;
typedef struct named_item_t {
- jsdisp_t *script_obj; IDispatch *disp;
- unsigned ref;
Maybe ref counting would make sense in a separated patch?
DWORD flags; LPWSTR name;
@@ -213,6 +215,7 @@ typedef struct named_item_t { } named_item_t;
named_item_t*lookup_named_item(script_ctx_t*,const WCHAR*,unsigned) DECLSPEC_HIDDEN; +void release_named_item(named_item_t*) DECLSPEC_HIDDEN;
typedef struct { const WCHAR *name; @@ -243,6 +246,7 @@ struct jsdisp_t { DWORD prop_cnt; dispex_prop_t *props; script_ctx_t *ctx;
- named_item_t *named_item;
I'd rather avoid having it here. I think that we should almost always be getting it from bytecode. In some corner cases, jsexcept should be enough to fill the gap.
jsdisp_t *prototype;
@@ -433,6 +437,7 @@ struct _script_ctx_t { DWORD last_match_length;
jsdisp_t *global;
- jsdisp_t *item_context;
Same here, do we really need it?
Thanks,
Jacek
Hi Jacek,
On 20/02/2020 22:41, Jacek Caban wrote:
Hi Gabriel,
First of all, this patch breaks mshtml script tests. It's likely that it's mshtml's fault, but we need to get it working.
Ok I'll take a look into it, didn't know it could.
On 19.02.2020 17:38, Gabriel Ivăncescu wrote:
-void enter_script(script_ctx_t *ctx, jsexcept_t *ei) +HRESULT enter_script(script_ctx_t *ctx, named_item_t *item, jsexcept_t *ei) {
- HRESULT hres;
memset(ei, 0, sizeof(*ei));
- if(item) {
if(!item->script_obj) {
hres = create_named_item_script_obj(ctx, item);
if(FAILED(hres)) return hres;
}
Can we have it in exec_source instead? If nothing else, it would simplify enter_script error handling.
I think it can, yes. Do you mean to also remove it from leave_script, right? (I mean, either set it in exec_source and unset at the end of the function, or find a way to retrieve it otherwise -- currently I use a "stack", but that's probably not necessary)
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 8297838..9492f3f 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -205,7 +205,9 @@ typedef HRESULT (*builtin_setter_t)(script_ctx_t*,jsdisp_t*,jsval_t); HRESULT builtin_set_const(script_ctx_t*,jsdisp_t*,jsval_t) DECLSPEC_HIDDEN;
typedef struct named_item_t {
- jsdisp_t *script_obj; IDispatch *disp;
- unsigned ref;
Maybe ref counting would make sense in a separated patch?
Right, I'll split it up.
DWORD flags; LPWSTR name;
@@ -213,6 +215,7 @@ typedef struct named_item_t { } named_item_t;
named_item_t*lookup_named_item(script_ctx_t*,const WCHAR*,unsigned) DECLSPEC_HIDDEN; +void release_named_item(named_item_t*) DECLSPEC_HIDDEN;
typedef struct { const WCHAR *name; @@ -243,6 +246,7 @@ struct jsdisp_t { DWORD prop_cnt; dispex_prop_t *props; script_ctx_t *ctx;
- named_item_t *named_item;
I'd rather avoid having it here. I think that we should almost always be getting it from bytecode. In some corner cases, jsexcept should be enough to fill the gap.
The only issue I had when I tried without it is in dispex.c/DispatchEx_InvokeEx. Is there another way to retrieve it, or the bytecode, in there?
jsdisp_t *prototype;
@@ -433,6 +437,7 @@ struct _script_ctx_t { DWORD last_match_length;
jsdisp_t *global;
- jsdisp_t *item_context;
Same here, do we really need it?
Probably not, but I'm not that familiar with the code. What's the easiest or best way to retrieve it from the bytecode? From call_ctx? Or perhaps from the ei?
Thanks, Gabriel
On 21.02.2020 13:55, Gabriel Ivăncescu wrote:
Hi Jacek,
On 20/02/2020 22:41, Jacek Caban wrote:
Hi Gabriel,
First of all, this patch breaks mshtml script tests. It's likely that it's mshtml's fault, but we need to get it working.
Ok I'll take a look into it, didn't know it could.
Yeah, mshtml is the main and most complex active script user we have in tree. It can host multiple script with in multiple languages, so vbscript can call jscript, etc. All script languages share global namespace and may call each other. This is done by mshtml window object exposing properties of all hosted script engines. To make it a bit more complicated, there may be multiple frames (like <frame> and <iframe> elements) and scripts in these don't share script engine nor global namespace with other frames but may access them using DOM API. The code doing that in mshtml could use some work, but I'm not sure if this part is responsible for the problem, only testing could tell.
On top of that native IE since version 9 rewrote JavaScript engine. In recent version, jscript.dll is not used for mshtml. To support new features that are present in native jscript.dll, we extended active script versioning API to add new flags that control those features. This is still work in progress. Quite a few additional APIs are implemented, but a lot more are missing. The main known problem is how objects are handling because. Making mshtml objects behave more like JavaScript objects needs a better mechanism than IDispatchEx. I have some ideas about that, but never had time to implement it.
Anyway, I'm saying that because what we observe in mshtml is not necessarily matching active script variant (because we know that native is not limited by active script). We have quite a few tests about how active scripts are handled in script.c, it should allow confirming here the problem is.
On 19.02.2020 17:38, Gabriel Ivăncescu wrote:
-void enter_script(script_ctx_t *ctx, jsexcept_t *ei) +HRESULT enter_script(script_ctx_t *ctx, named_item_t *item, jsexcept_t *ei) { + HRESULT hres;
memset(ei, 0, sizeof(*ei)); + if(item) { + if(!item->script_obj) { + hres = create_named_item_script_obj(ctx, item); + if(FAILED(hres)) return hres; + }
Can we have it in exec_source instead? If nothing else, it would simplify enter_script error handling.
I think it can, yes. Do you mean to also remove it from leave_script, right? (I mean, either set it in exec_source and unset at the end of the function, or find a way to retrieve it otherwise -- currently I use a "stack", but that's probably not necessary)
I hope we can just get rid of item_context from script context, which would make leave_script change obsolete.
DWORD flags; LPWSTR name; @@ -213,6 +215,7 @@ typedef struct named_item_t { } named_item_t; named_item_t*lookup_named_item(script_ctx_t*,const WCHAR*,unsigned) DECLSPEC_HIDDEN; +void release_named_item(named_item_t*) DECLSPEC_HIDDEN; typedef struct { const WCHAR *name; @@ -243,6 +246,7 @@ struct jsdisp_t { DWORD prop_cnt; dispex_prop_t *props; script_ctx_t *ctx; + named_item_t *named_item;
I'd rather avoid having it here. I think that we should almost always be getting it from bytecode. In some corner cases, jsexcept should be enough to fill the gap.
The only issue I had when I tried without it is in dispex.c/DispatchEx_InvokeEx. Is there another way to retrieve it, or the bytecode, in there?
See below.
jsdisp_t *prototype; @@ -433,6 +437,7 @@ struct _script_ctx_t { DWORD last_match_length; jsdisp_t *global; + jsdisp_t *item_context;
Same here, do we really need it?
Probably not, but I'm not that familiar with the code. What's the easiest or best way to retrieve it from the bytecode? From call_ctx? Or perhaps from the ei?
When you're in interpreter, yes. Otherwise, it depends. Once you get rid of variables I mentioned and start using context from bytecode, it should become clear what we need to worry about. I sense that the problematic areas will be where builtin functions need to parse source.
Thanks,
Jacek
Hi Jacek,
On 21/02/2020 19:31, Jacek Caban wrote:
On 21.02.2020 13:55, Gabriel Ivăncescu wrote:
Hi Jacek,
On 20/02/2020 22:41, Jacek Caban wrote:
Hi Gabriel,
First of all, this patch breaks mshtml script tests. It's likely that it's mshtml's fault, but we need to get it working.
Ok I'll take a look into it, didn't know it could.
Yeah, mshtml is the main and most complex active script user we have in tree. It can host multiple script with in multiple languages, so vbscript can call jscript, etc. All script languages share global namespace and may call each other. This is done by mshtml window object exposing properties of all hosted script engines. To make it a bit more complicated, there may be multiple frames (like <frame> and <iframe> elements) and scripts in these don't share script engine nor global namespace with other frames but may access them using DOM API. The code doing that in mshtml could use some work, but I'm not sure if this part is responsible for the problem, only testing could tell.
On top of that native IE since version 9 rewrote JavaScript engine. In recent version, jscript.dll is not used for mshtml. To support new features that are present in native jscript.dll, we extended active script versioning API to add new flags that control those features. This is still work in progress. Quite a few additional APIs are implemented, but a lot more are missing. The main known problem is how objects are handling because. Making mshtml objects behave more like JavaScript objects needs a better mechanism than IDispatchEx. I have some ideas about that, but never had time to implement it.
Anyway, I'm saying that because what we observe in mshtml is not necessarily matching active script variant (because we know that native is not limited by active script). We have quite a few tests about how active scripts are handled in script.c, it should allow confirming here the problem is.
Thanks, it appears that there's a corner case with SCRIPTITEM_GLOBALMEMBERS and the script dispatch, and this applies to both jscript and vbscript, so mshtml doesn't seem to be wrong here. I will of course add tests to both of them to show this behavior.
While investigating this I also found out another behavior for SCRIPTITEM_CODEONLY (again for both jscript and vbscript), namely that the 'this' or 'me' returns the item's disp, and only returns the script_obj if the flag is set.
I'll do some more testing just to make sure I'm not missing something, but I have a general idea of how to fix both cleanly.
On 19.02.2020 17:38, Gabriel Ivăncescu wrote:
-void enter_script(script_ctx_t *ctx, jsexcept_t *ei) +HRESULT enter_script(script_ctx_t *ctx, named_item_t *item, jsexcept_t *ei) { + HRESULT hres;
memset(ei, 0, sizeof(*ei)); + if(item) { + if(!item->script_obj) { + hres = create_named_item_script_obj(ctx, item); + if(FAILED(hres)) return hres; + }
Can we have it in exec_source instead? If nothing else, it would simplify enter_script error handling.
I think it can, yes. Do you mean to also remove it from leave_script, right? (I mean, either set it in exec_source and unset at the end of the function, or find a way to retrieve it otherwise -- currently I use a "stack", but that's probably not necessary)
I hope we can just get rid of item_context from script context, which would make leave_script change obsolete.
Yeah, I got rid of it, and now I obtain it from ctx->ei->named_item.
The reason I need to store it in the ei (along with the "stack" it creates on each enter_script/leave_script) is because it has to be accessed from many parts, not just exec_source, but also 'eval', compile_script, InvokeEx, and we can't pass a parameter from there.
However, it doesn't need any changes to enter_script/leave_script anymore, so it's much cleaner.
DWORD flags; LPWSTR name; @@ -213,6 +215,7 @@ typedef struct named_item_t { } named_item_t; named_item_t*lookup_named_item(script_ctx_t*,const WCHAR*,unsigned) DECLSPEC_HIDDEN; +void release_named_item(named_item_t*) DECLSPEC_HIDDEN; typedef struct { const WCHAR *name; @@ -243,6 +246,7 @@ struct jsdisp_t { DWORD prop_cnt; dispex_prop_t *props; script_ctx_t *ctx; + named_item_t *named_item;
I'd rather avoid having it here. I think that we should almost always be getting it from bytecode. In some corner cases, jsexcept should be enough to fill the gap.
The only issue I had when I tried without it is in dispex.c/DispatchEx_InvokeEx. Is there another way to retrieve it, or the bytecode, in there?
See below.
jsdisp_t *prototype; @@ -433,6 +437,7 @@ struct _script_ctx_t { DWORD last_match_length; jsdisp_t *global; + jsdisp_t *item_context;
Same here, do we really need it?
Probably not, but I'm not that familiar with the code. What's the easiest or best way to retrieve it from the bytecode? From call_ctx? Or perhaps from the ei?
When you're in interpreter, yes. Otherwise, it depends. Once you get rid of variables I mentioned and start using context from bytecode, it should become clear what we need to worry about. I sense that the problematic areas will be where builtin functions need to parse source.
Right, currently I have it so the context from bytecode sets the ei->named_item in exec_source at the start and also creates the script dispatch on demand.
The ei->named_item is also set before compiling the script in ParseScriptText/ParseProcedureText, but it is automatically picked in 'eval' though (which also needs compilation).
On 21.02.2020 19:49, Gabriel Ivăncescu wrote:
Hi Jacek,
On 21/02/2020 19:31, Jacek Caban wrote:
On 21.02.2020 13:55, Gabriel Ivăncescu wrote:
Hi Jacek,
On 20/02/2020 22:41, Jacek Caban wrote:
Hi Gabriel,
First of all, this patch breaks mshtml script tests. It's likely that it's mshtml's fault, but we need to get it working.
Ok I'll take a look into it, didn't know it could.
Yeah, mshtml is the main and most complex active script user we have in tree. It can host multiple script with in multiple languages, so vbscript can call jscript, etc. All script languages share global namespace and may call each other. This is done by mshtml window object exposing properties of all hosted script engines. To make it a bit more complicated, there may be multiple frames (like <frame> and
<iframe> elements) and scripts in these don't share script engine nor global namespace with other frames but may access them using DOM API. The code doing that in mshtml could use some work, but I'm not sure if this part is responsible for the problem, only testing could tell.
On top of that native IE since version 9 rewrote JavaScript engine. In recent version, jscript.dll is not used for mshtml. To support new features that are present in native jscript.dll, we extended active script versioning API to add new flags that control those features. This is still work in progress. Quite a few additional APIs are implemented, but a lot more are missing. The main known problem is how objects are handling because. Making mshtml objects behave more like JavaScript objects needs a better mechanism than IDispatchEx. I have some ideas about that, but never had time to implement it.
Anyway, I'm saying that because what we observe in mshtml is not necessarily matching active script variant (because we know that native is not limited by active script). We have quite a few tests about how active scripts are handled in script.c, it should allow confirming here the problem is.
Thanks, it appears that there's a corner case with SCRIPTITEM_GLOBALMEMBERS and the script dispatch, and this applies to both jscript and vbscript, so mshtml doesn't seem to be wrong here. I will of course add tests to both of them to show this behavior.
While investigating this I also found out another behavior for SCRIPTITEM_CODEONLY (again for both jscript and vbscript), namely that the 'this' or 'me' returns the item's disp, and only returns the script_obj if the flag is set.
I'll do some more testing just to make sure I'm not missing something, but I have a general idea of how to fix both cleanly.
Good to hear it, thanks.
jsdisp_t *prototype; @@ -433,6 +437,7 @@ struct _script_ctx_t { DWORD last_match_length; jsdisp_t *global; + jsdisp_t *item_context;
Same here, do we really need it?
Probably not, but I'm not that familiar with the code. What's the easiest or best way to retrieve it from the bytecode? From call_ctx? Or perhaps from the ei?
When you're in interpreter, yes. Otherwise, it depends. Once you get rid of variables I mentioned and start using context from bytecode, it should become clear what we need to worry about. I sense that the problematic areas will be where builtin functions need to parse source.
Right, currently I have it so the context from bytecode sets the ei->named_item in exec_source at the start and also creates the script dispatch on demand.
The ei->named_item is also set before compiling the script in ParseScriptText/ParseProcedureText, but it is automatically picked in 'eval' though (which also needs compilation).
Let me explain problem with ei (where "ei" should really be something like caller_context after recent changes, but I didn't do renaming yet wanting to make sure that's how we want it first). It is set when we enter the script and shared by internal script calls as long as they don't leave or re-enter the script. So let's say we have contexts ctx1 and ctx2, both exposing ctx_id property and a global variable get_ctx_id.
In side ctx1, we execute:
get_ctx_id = function() { return ctx_id; };
And now inside ctx2, we execute:
var x = get_ctx_id();
What will be the value of ctx_id? It's parsed in context of ctx1, so I'd expect it to be ctx1.ctx_id. But it was called by code inside ctx2 and we never left interpreter, so ei will still point to ctx2, so you may get ctx2.ctx_id.
That said, maybe there is some use for ei. But it's not generally what you need.
Jacek
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/tests/jscript.c | 597 ++++++++++++++++++++++++++++++++++- 1 file changed, 584 insertions(+), 13 deletions(-)
diff --git a/dlls/jscript/tests/jscript.c b/dlls/jscript/tests/jscript.c index 84805b1..8789bb2 100644 --- a/dlls/jscript/tests/jscript.c +++ b/dlls/jscript/tests/jscript.c @@ -97,6 +97,12 @@ DEFINE_EXPECT(OnStateChange_CLOSED); DEFINE_EXPECT(OnStateChange_INITIALIZED); DEFINE_EXPECT(OnEnterScript); DEFINE_EXPECT(OnLeaveScript); +DEFINE_EXPECT(OnScriptError); +DEFINE_EXPECT(GetIDsOfNames); +DEFINE_EXPECT(GetItemInfo_global); +DEFINE_EXPECT(GetItemInfo_visible); +DEFINE_EXPECT(GetItemInfo_visible_code); +DEFINE_EXPECT(testCall);
static const CLSID *engine_clsid = &CLSID_JScript;
@@ -111,6 +117,121 @@ static void _test_state(unsigned line, IActiveScript *script, SCRIPTSTATE exstat ok_(__FILE__,line) (state == exstate, "state=%d, expected %d\n", state, exstate); }
+static HRESULT WINAPI Dispatch_QueryInterface(IDispatch *iface, REFIID riid, void **ppv) +{ + if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IDispatch, riid)) { + *ppv = iface; + IDispatch_AddRef(iface); + return S_OK; + } + + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG global_named_item_ref, visible_named_item_ref, visible_code_named_item_ref; + +static ULONG WINAPI global_AddRef(IDispatch *iface) +{ + return ++global_named_item_ref; +} + +static ULONG WINAPI global_Release(IDispatch *iface) +{ + return --global_named_item_ref; +} + +static ULONG WINAPI visible_AddRef(IDispatch *iface) +{ + return ++visible_named_item_ref; +} + +static ULONG WINAPI visible_Release(IDispatch *iface) +{ + return --visible_named_item_ref; +} + +static ULONG WINAPI visible_code_AddRef(IDispatch *iface) +{ + return ++visible_code_named_item_ref; +} + +static ULONG WINAPI visible_code_Release(IDispatch *iface) +{ + return --visible_code_named_item_ref; +} + +static HRESULT WINAPI Dispatch_GetTypeInfoCount(IDispatch *iface, UINT *pctinfo) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Dispatch_GetTypeInfo(IDispatch *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) +{ + return DISP_E_BADINDEX; +} + +static HRESULT WINAPI Dispatch_GetIDsOfNames(IDispatch *iface, REFIID riid, LPOLESTR *names, UINT name_cnt, + LCID lcid, DISPID *ids) +{ + ok(name_cnt == 1, "name_cnt = %u\n", name_cnt); + if(!wcscmp(names[0], L"testCall")) { + *ids = 1; + return S_OK; + } + + CHECK_EXPECT2(GetIDsOfNames); + return DISP_E_UNKNOWNNAME; +} + +static HRESULT WINAPI Dispatch_Invoke(IDispatch *iface, DISPID id, REFIID riid, LCID lcid, WORD flags, + DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, UINT *err) +{ + CHECK_EXPECT(testCall); + ok(id == 1, "id = %u\n", id); + ok(flags == DISPATCH_METHOD, "flags = %x\n", flags); + ok(!dp->cArgs, "cArgs = %u\n", dp->cArgs); + ok(!res, "res = %p\n", res); + return S_OK; +} + +static const IDispatchVtbl global_named_item_vtbl = { + Dispatch_QueryInterface, + global_AddRef, + global_Release, + Dispatch_GetTypeInfoCount, + Dispatch_GetTypeInfo, + Dispatch_GetIDsOfNames, + Dispatch_Invoke +}; + +static IDispatch global_named_item = { &global_named_item_vtbl }; + +static const IDispatchVtbl visible_named_item_vtbl = { + Dispatch_QueryInterface, + visible_AddRef, + visible_Release, + Dispatch_GetTypeInfoCount, + Dispatch_GetTypeInfo, + Dispatch_GetIDsOfNames, + Dispatch_Invoke +}; + +static IDispatch visible_named_item = { &visible_named_item_vtbl }; + +static const IDispatchVtbl visible_code_named_item_vtbl = { + Dispatch_QueryInterface, + visible_code_AddRef, + visible_code_Release, + Dispatch_GetTypeInfoCount, + Dispatch_GetTypeInfo, + Dispatch_GetIDsOfNames, + Dispatch_Invoke +}; + +static IDispatch visible_code_named_item = { &visible_code_named_item_vtbl }; + static HRESULT WINAPI ActiveScriptSite_QueryInterface(IActiveScriptSite *iface, REFIID riid, void **ppv) { *ppv = NULL; @@ -145,7 +266,26 @@ static HRESULT WINAPI ActiveScriptSite_GetLCID(IActiveScriptSite *iface, LCID *p static HRESULT WINAPI ActiveScriptSite_GetItemInfo(IActiveScriptSite *iface, LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown **ppiunkItem, ITypeInfo **ppti) { - ok(0, "unexpected call\n"); + ok(dwReturnMask == SCRIPTINFO_IUNKNOWN, "dwReturnMask = %x\n", dwReturnMask); + if(!wcscmp(pstrName, L"globalItem")) { + CHECK_EXPECT(GetItemInfo_global); + IDispatch_AddRef(&global_named_item); + *ppiunkItem = (IUnknown*)&global_named_item; + return S_OK; + } + if(!wcscmp(pstrName, L"visibleItem")) { + CHECK_EXPECT(GetItemInfo_visible); + IDispatch_AddRef(&visible_named_item); + *ppiunkItem = (IUnknown*)&visible_named_item; + return S_OK; + } + if(!wcscmp(pstrName, L"visibleCodeItem")) { + CHECK_EXPECT(GetItemInfo_visible_code); + IDispatch_AddRef(&visible_code_named_item); + *ppiunkItem = (IUnknown*)&visible_code_named_item; + return S_OK; + } + ok(0, "unexpected call %s\n", wine_dbgstr_w(pstrName)); return E_NOTIMPL; }
@@ -192,8 +332,8 @@ static HRESULT WINAPI ActiveScriptSite_OnStateChange(IActiveScriptSite *iface, S
static HRESULT WINAPI ActiveScriptSite_OnScriptError(IActiveScriptSite *iface, IActiveScriptError *pscripterror) { - ok(0, "unexpected call\n"); - return E_NOTIMPL; + CHECK_EXPECT(OnScriptError); + return S_OK; }
static HRESULT WINAPI ActiveScriptSite_OnEnterScript(IActiveScriptSite *iface) @@ -263,14 +403,14 @@ static void test_script_dispatch(IDispatchEx *dispex) VariantClear(&v); }
-static IDispatchEx *get_script_dispatch(IActiveScript *script) +static IDispatchEx *get_script_dispatch(IActiveScript *script, const WCHAR *item_name) { IDispatchEx *dispex; IDispatch *disp; HRESULT hres;
disp = (void*)0xdeadbeef; - hres = IActiveScript_GetScriptDispatch(script, NULL, &disp); + hres = IActiveScript_GetScriptDispatch(script, item_name, &disp); ok(hres == S_OK, "GetScriptDispatch failed: %08x\n", hres);
hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); @@ -512,7 +652,7 @@ static void test_jscript(void) hres = IActiveScript_SetScriptSite(script, &ActiveScriptSite); ok(hres == E_UNEXPECTED, "SetScriptSite failed: %08x, expected E_UNEXPECTED\n", hres);
- dispex = get_script_dispatch(script); + dispex = get_script_dispatch(script, NULL); test_script_dispatch(dispex);
SET_EXPECT(OnStateChange_STARTED); @@ -656,7 +796,7 @@ static void test_jscript_uninitializing(void)
test_state(script, SCRIPTSTATE_CONNECTED);
- dispex = get_script_dispatch(script); + dispex = get_script_dispatch(script, NULL); ok(dispex != NULL, "dispex == NULL\n"); IDispatchEx_Release(dispex);
@@ -741,7 +881,7 @@ static void test_code_persistence(void) ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr);
/* Pending code does not add identifiers to the global scope */ - dispex = get_script_dispatch(script); + dispex = get_script_dispatch(script, NULL); id = 0; get_disp_id(dispex, L"x", DISP_E_UNKNOWNNAME, &id); ok(id == -1, "id = %d, expected -1\n", id); @@ -780,7 +920,7 @@ static void test_code_persistence(void) CHECK_CALLED_MULTI(OnLeaveScript, 2); test_state(script, SCRIPTSTATE_CONNECTED);
- dispex = get_script_dispatch(script); + dispex = get_script_dispatch(script, NULL); id = 0; get_disp_id(dispex, L"x", DISP_E_UNKNOWNNAME, &id); ok(id == -1, "id = %d, expected -1\n", id); @@ -826,7 +966,7 @@ static void test_code_persistence(void) CHECK_CALLED(GetLCID); CHECK_CALLED(OnStateChange_INITIALIZED);
- dispex = get_script_dispatch(script); + dispex = get_script_dispatch(script, NULL); id = 0; get_disp_id(dispex, L"z", DISP_E_UNKNOWNNAME, &id); ok(id == -1, "id = %d, expected -1\n", id); @@ -842,7 +982,7 @@ static void test_code_persistence(void) CHECK_CALLED(OnLeaveScript); test_state(script, SCRIPTSTATE_CONNECTED);
- dispex = get_script_dispatch(script); + dispex = get_script_dispatch(script, NULL); id = 0; get_disp_id(dispex, L"z", S_OK, &id); ok(id != -1, "id = -1\n"); @@ -905,7 +1045,7 @@ static void test_code_persistence(void) CHECK_CALLED(OnStateChange_CONNECTED); test_state(script, SCRIPTSTATE_CONNECTED);
- dispex = get_script_dispatch(script); + dispex = get_script_dispatch(script, NULL); id = 0; get_disp_id(dispex, L"y", DISP_E_UNKNOWNNAME, &id); ok(id == -1, "id = %d, expected -1\n", id); @@ -926,6 +1066,436 @@ static void test_code_persistence(void) CHECK_CALLED(OnStateChange_CLOSED); }
+static void test_named_items(void) +{ + static const WCHAR *global_idents[] = + { + L"ActiveXObject", + L"Array", + L"Boolean", + L"Date", + L"Enumerator", + L"Error", + L"eval", + L"EvalError", + L"Function", + L"Infinity", + L"Math", + L"NaN", + L"Number", + L"Object", + L"RangeError", + L"ReferenceError", + L"RegExp", + L"RegExpError", + L"String", + L"SyntaxError", + L"TypeError", + L"undefined", + L"URIError", + L"VBArray", + + L"CollectGarbage", + L"decodeURI", + L"decodeURIComponent", + L"encodeURI", + L"encodeURIComponent", + L"escape", + L"eval", + L"isFinite", + L"isNaN", + L"parseFloat", + L"parseInt", + L"ScriptEngine", + L"ScriptEngineBuildVersion", + L"ScriptEngineMajorVersion", + L"ScriptEngineMinorVersion", + L"unescape", + + L"testFunc_global", + L"testVar_global" + }; + static const WCHAR *global_code_test[] = + { + L"testFunc_global();", + L"if(testVar_global != 5) throw new Error();", + L"var testObj = new testClassFunc();", + L"eval("testFunc_global();");", + L"if(Math.abs(-17) != 17) throw new Error();" + }; + static const WCHAR *context_idents[] = + { + L"testFunc", + L"testVar" + }; + static const WCHAR *context_code_test[] = + { + L"testFunc();", + L"if(testVar != 42) throw new Error();", + L"if(Math.abs(-testVar) != 42) throw new Error();" + }; + IDispatchEx *dispex, *dispex2; + IActiveScriptParse *parse; + IActiveScript *script; + IDispatch *disp; + VARIANT var; + unsigned i; + HRESULT hr; + DISPID id; + ULONG ref; + BSTR bstr; + + script = create_jscript(); + + hr = IActiveScript_QueryInterface(script, &IID_IActiveScriptParse, (void**)&parse); + ok(hr == S_OK, "Could not get IActiveScriptParse: %08x\n", hr); + + test_state(script, SCRIPTSTATE_UNINITIALIZED); + + hr = IActiveScript_AddNamedItem(script, L"visibleItem", SCRIPTITEM_ISVISIBLE); + ok(hr == E_UNEXPECTED, "AddNamedItem returned: %08x\n", hr); + hr = IActiveScript_AddNamedItem(script, L"globalItem", SCRIPTITEM_GLOBALMEMBERS); + ok(hr == E_UNEXPECTED, "AddNamedItem returned: %08x\n", hr); + hr = IActiveScript_AddNamedItem(script, L"codeOnlyItem", SCRIPTITEM_CODEONLY); + ok(hr == E_UNEXPECTED, "AddNamedItem returned: %08x\n", hr); + + SET_EXPECT(GetLCID); + hr = IActiveScript_SetScriptSite(script, &ActiveScriptSite); + ok(hr == S_OK, "SetScriptSite failed: %08x\n", hr); + CHECK_CALLED(GetLCID); + + SET_EXPECT(GetItemInfo_global); + hr = IActiveScript_AddNamedItem(script, L"globalItem", SCRIPTITEM_GLOBALMEMBERS); + ok(hr == S_OK, "AddNamedItem failed: %08x\n", hr); + CHECK_CALLED(GetItemInfo_global); + + hr = IActiveScript_AddNamedItem(script, L"visibleItem", SCRIPTITEM_ISVISIBLE); + ok(hr == S_OK, "AddNamedItem failed: %08x\n", hr); + hr = IActiveScript_AddNamedItem(script, L"visibleCodeItem", SCRIPTITEM_ISVISIBLE | SCRIPTITEM_CODEONLY); + ok(hr == S_OK, "AddNamedItem failed: %08x\n", hr); + hr = IActiveScript_AddNamedItem(script, L"codeOnlyItem", SCRIPTITEM_CODEONLY); + ok(hr == S_OK, "AddNamedItem failed: %08x\n", hr); + + ok(global_named_item_ref > 0, "global_named_item_ref = %u\n", global_named_item_ref); + ok(visible_named_item_ref == 0, "visible_named_item_ref = %u\n", visible_named_item_ref); + ok(visible_code_named_item_ref == 0, "visible_code_named_item_ref = %u\n", visible_code_named_item_ref); + + hr = IActiveScript_GetScriptDispatch(script, L"noContext", &disp); + ok(hr == E_INVALIDARG, "GetScriptDispatch returned: %08x\n", hr); + hr = IActiveScript_GetScriptDispatch(script, L"codeONLYItem", &disp); + ok(hr == E_INVALIDARG, "GetScriptDispatch returned: %08x\n", hr); + + dispex = get_script_dispatch(script, NULL); + dispex2 = get_script_dispatch(script, L"codeOnlyItem"); + ok(dispex != dispex2, "get_script_dispatch returned same dispatch objects.\n"); + + SET_EXPECT(OnStateChange_INITIALIZED); + hr = IActiveScriptParse_InitNew(parse); + ok(hr == S_OK, "InitNew failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_INITIALIZED); + + SET_EXPECT(OnStateChange_CONNECTED); + hr = IActiveScript_SetScriptState(script, SCRIPTSTATE_CONNECTED); + ok(hr == S_OK, "SetScriptState(SCRIPTSTATE_CONNECTED) failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_CONNECTED); + + SET_EXPECT(testCall); + parse_script(parse, L"testCall();"); + CHECK_CALLED(testCall); + + SET_EXPECT(GetItemInfo_visible); + SET_EXPECT(testCall); + parse_script(parse, L"visibleItem.testCall();"); + CHECK_CALLED(GetItemInfo_visible); + CHECK_CALLED(testCall); + + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + SET_EXPECT(testCall); + hr = IActiveScriptParse_ParseScriptText(parse, L"testCall();", L"visibleCodeItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + CHECK_CALLED(testCall); + + SET_EXPECT(OnEnterScript); + SET_EXPECT(GetIDsOfNames); + SET_EXPECT(OnScriptError); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, L"codeOnlyItem();", L"codeOnlyItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(FAILED(hr), "ParseScriptText returned: %08x\n", hr); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(GetIDsOfNames); + CHECK_CALLED(OnScriptError); + CHECK_CALLED(OnLeaveScript); + + hr = IActiveScript_GetScriptDispatch(script, L"visibleCodeItem", &disp); + ok(hr == S_OK, "GetScriptDispatch returned: %08x\n", hr); + IDispatch_Release(disp); + + SET_EXPECT(GetItemInfo_visible_code); + SET_EXPECT(testCall); + parse_script(parse, L"visibleCodeItem.testCall();"); + CHECK_CALLED(GetItemInfo_visible_code); + CHECK_CALLED(testCall); + + ok(global_named_item_ref > 0, "global_named_item_ref = %u\n", global_named_item_ref); + ok(visible_named_item_ref > 0, "visible_named_item_ref = %u\n", visible_named_item_ref); + ok(visible_code_named_item_ref > 0, "visible_code_named_item_ref = %u\n", visible_code_named_item_ref); + + SET_EXPECT(testCall); + parse_script(parse, L"visibleItem.testCall();"); + CHECK_CALLED(testCall); + + hr = IActiveScriptParse_ParseScriptText(parse, L"function testFunc() { }", L"CodeOnlyItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hr == E_INVALIDARG, "ParseScriptText returned: %08x\n", hr); + SET_EXPECT(OnEnterScript); + SET_EXPECT(GetIDsOfNames); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, L"" + "function testFunc_global() { }\n" + "var testVar_global = 10;\n" + "function testClassFunc() { this.x = 10; }\n", + NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISPERSISTENT, NULL, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(GetIDsOfNames); + CHECK_CALLED(OnLeaveScript); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, L"function testFunc() { }\n", L"codeOnlyItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, L"" + "var testVar = 42;\n" + "testVar_global = 5;\n", + L"codeOnlyItem", NULL, NULL, 0, 0, SCRIPTTEXT_ISPERSISTENT, NULL, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + + for (i = 0; i < ARRAY_SIZE(global_idents); i++) + { + bstr = SysAllocString(global_idents[i]); + id = 0; + hr = IDispatchEx_GetDispID(dispex, bstr, 0, &id); + ok(hr == S_OK, "GetDispID(%s) returned %08x\n", wine_dbgstr_w(global_idents[i]), hr); + ok(id != -1, "[%s] id = -1\n", wine_dbgstr_w(global_idents[i])); + id = 0; + hr = IDispatchEx_GetDispID(dispex2, bstr, 0, &id); + ok(hr == DISP_E_UNKNOWNNAME, "GetDispID(%s) returned %08x\n", wine_dbgstr_w(global_idents[i]), hr); + ok(id == -1, "[%s] id = %d, expected -1\n", wine_dbgstr_w(global_idents[i]), id); + SysFreeString(bstr); + } + for (i = 0; i < ARRAY_SIZE(context_idents); i++) + { + bstr = SysAllocString(context_idents[i]); + id = 0; + hr = IDispatchEx_GetDispID(dispex, bstr, 0, &id); + ok(hr == DISP_E_UNKNOWNNAME, "GetDispID(%s) returned %08x\n", wine_dbgstr_w(context_idents[i]), hr); + ok(id == -1, "[%s] id = %d, expected -1\n", wine_dbgstr_w(context_idents[i]), id); + id = 0; + hr = IDispatchEx_GetDispID(dispex2, bstr, 0, &id); + ok(hr == S_OK, "GetDispID(%s) returned %08x\n", wine_dbgstr_w(context_idents[i]), hr); + ok(id != -1, "[%s] id = -1\n", wine_dbgstr_w(context_idents[i])); + SysFreeString(bstr); + } + + for (i = 0; i < ARRAY_SIZE(global_code_test); i++) + { + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, global_code_test[i], NULL, NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hr == S_OK, "ParseScriptText(%s) failed: %08x\n", wine_dbgstr_w(global_code_test[i]), hr); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + SET_EXPECT(OnEnterScript); + SET_EXPECT(GetIDsOfNames); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, global_code_test[i], L"codeOnlyItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hr == S_OK, "ParseScriptText(%s) failed: %08x\n", wine_dbgstr_w(global_code_test[i]), hr); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + } + for (i = 0; i < ARRAY_SIZE(context_code_test); i++) + { + SET_EXPECT(OnEnterScript); + SET_EXPECT(GetIDsOfNames); + SET_EXPECT(OnScriptError); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, context_code_test[i], NULL, NULL, NULL, 0, 0, 0, NULL, NULL); + ok(FAILED(hr), "ParseScriptText(%s) returned: %08x\n", wine_dbgstr_w(context_code_test[i]), hr); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(GetIDsOfNames); + CHECK_CALLED(OnScriptError); + CHECK_CALLED(OnLeaveScript); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, context_code_test[i], L"codeOnlyItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hr == S_OK, "ParseScriptText(%s) failed: %08x\n", wine_dbgstr_w(context_code_test[i]), hr); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + } + + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, L"this", NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &var, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + ok(V_VT(&var) == VT_DISPATCH && V_DISPATCH(&var) == &global_named_item, + "Unexpected 'this': V_VT = %d, V_DISPATCH = %p\n", V_VT(&var), V_DISPATCH(&var)); + VariantClear(&var); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, L"this", L"codeOnlyItem", NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &var, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + ok(V_VT(&var) == VT_DISPATCH && V_DISPATCH(&var) == (IDispatch*)dispex2, + "Unexpected 'this': V_VT = %d, V_DISPATCH = %p\n", V_VT(&var), V_DISPATCH(&var)); + VariantClear(&var); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + + IDispatchEx_Release(dispex2); + IDispatchEx_Release(dispex); + + SET_EXPECT(OnStateChange_DISCONNECTED); + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(OnStateChange_UNINITIALIZED); + hr = IActiveScript_SetScriptState(script, SCRIPTSTATE_UNINITIALIZED); + ok(hr == S_OK, "SetScriptState(SCRIPTSTATE_UNINITIALIZED) failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_DISCONNECTED); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(OnStateChange_UNINITIALIZED); + test_no_script_dispatch(script); + + ok(global_named_item_ref == 0, "global_named_item_ref = %u\n", global_named_item_ref); + ok(visible_named_item_ref == 0, "visible_named_item_ref = %u\n", visible_named_item_ref); + ok(visible_code_named_item_ref == 0, "visible_code_named_item_ref = %u\n", visible_code_named_item_ref); + + hr = IActiveScript_GetScriptDispatch(script, L"codeOnlyItem", &disp); + ok(hr == E_UNEXPECTED, "hr = %08x, expected E_UNEXPECTED\n", hr); + + SET_EXPECT(GetLCID); + SET_EXPECT(OnStateChange_INITIALIZED); + hr = IActiveScript_SetScriptSite(script, &ActiveScriptSite); + ok(hr == S_OK, "SetScriptSite failed: %08x\n", hr); + CHECK_CALLED(GetLCID); + CHECK_CALLED(OnStateChange_INITIALIZED); + + hr = IActiveScript_AddNamedItem(script, L"codeOnlyItem", SCRIPTITEM_CODEONLY); + ok(hr == S_OK, "AddNamedItem failed: %08x\n", hr); + + SET_EXPECT(OnStateChange_CONNECTED); + SET_EXPECT_MULTI(OnEnterScript, 2); + SET_EXPECT_MULTI(OnLeaveScript, 2); + hr = IActiveScript_SetScriptState(script, SCRIPTSTATE_CONNECTED); + ok(hr == S_OK, "SetScriptState(SCRIPTSTATE_CONNECTED) failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_CONNECTED); + CHECK_CALLED_MULTI(OnEnterScript, 2); + CHECK_CALLED_MULTI(OnLeaveScript, 2); + test_state(script, SCRIPTSTATE_CONNECTED); + + dispex = get_script_dispatch(script, NULL); + for (i = 0; i < ARRAY_SIZE(global_idents); i++) + { + bstr = SysAllocString(global_idents[i]); + id = 0; + hr = IDispatchEx_GetDispID(dispex, bstr, 0, &id); + ok(hr == S_OK, "GetDispID(%s) returned %08x\n", wine_dbgstr_w(global_idents[i]), hr); + ok(id != -1, "[%s] id = -1\n", wine_dbgstr_w(global_idents[i])); + SysFreeString(bstr); + } + for (i = 0; i < ARRAY_SIZE(context_idents); i++) + { + bstr = SysAllocString(context_idents[i]); + id = 0; + hr = IDispatchEx_GetDispID(dispex, bstr, 0, &id); + ok(hr == DISP_E_UNKNOWNNAME, "GetDispID(%s) returned %08x\n", wine_dbgstr_w(context_idents[i]), hr); + ok(id == -1, "[%s] id = %d, expected -1\n", wine_dbgstr_w(context_idents[i]), id); + SysFreeString(bstr); + } + IDispatchEx_Release(dispex); + + dispex = get_script_dispatch(script, L"codeOnlyItem"); + for (i = 0; i < ARRAY_SIZE(global_idents); i++) + { + bstr = SysAllocString(global_idents[i]); + id = 0; + hr = IDispatchEx_GetDispID(dispex, bstr, 0, &id); + ok(hr == DISP_E_UNKNOWNNAME, "GetDispID(%s) returned %08x\n", wine_dbgstr_w(global_idents[i]), hr); + ok(id == -1, "[%s] id = %d, expected -1\n", wine_dbgstr_w(global_idents[i]), id); + SysFreeString(bstr); + } + for (i = 0; i < ARRAY_SIZE(context_idents); i++) + { + bstr = SysAllocString(context_idents[i]); + id = 0; + hr = IDispatchEx_GetDispID(dispex, bstr, 0, &id); + ok(hr == DISP_E_UNKNOWNNAME, "GetDispID(%s) returned %08x\n", wine_dbgstr_w(context_idents[i]), hr); + ok(id == -1, "[%s] id = %d, expected -1\n", wine_dbgstr_w(context_idents[i]), id); + SysFreeString(bstr); + } + IDispatchEx_Release(dispex); + + for (i = 0; i < ARRAY_SIZE(global_code_test); i++) + { + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, global_code_test[i], NULL, NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hr == S_OK, "ParseScriptText(%s) failed: %08x\n", wine_dbgstr_w(global_code_test[i]), hr); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, global_code_test[i], L"codeOnlyItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hr == S_OK, "ParseScriptText(%s) failed: %08x\n", wine_dbgstr_w(global_code_test[i]), hr); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + } + for (i = 0; i < ARRAY_SIZE(context_code_test); i++) + { + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnScriptError); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, context_code_test[i], NULL, NULL, NULL, 0, 0, 0, NULL, NULL); + ok(FAILED(hr), "ParseScriptText(%s) returned: %08x\n", wine_dbgstr_w(context_code_test[i]), hr); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnScriptError); + CHECK_CALLED(OnLeaveScript); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnScriptError); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, context_code_test[i], L"codeOnlyItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(FAILED(hr), "ParseScriptText(%s) returned: %08x\n", wine_dbgstr_w(context_code_test[i]), hr); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnScriptError); + CHECK_CALLED(OnLeaveScript); + } + + SET_EXPECT(OnStateChange_DISCONNECTED); + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(OnStateChange_CLOSED); + hr = IActiveScript_Close(script); + ok(hr == S_OK, "Close failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_DISCONNECTED); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(OnStateChange_CLOSED); + + ok(global_named_item_ref == 0, "global_named_item_ref = %u\n", global_named_item_ref); + ok(visible_named_item_ref == 0, "visible_named_item_ref = %u\n", visible_named_item_ref); + ok(visible_code_named_item_ref == 0, "visible_code_named_item_ref = %u\n", visible_code_named_item_ref); + + test_state(script, SCRIPTSTATE_CLOSED); + IActiveScriptParse_Release(parse); + + ref = IActiveScript_Release(script); + ok(!ref, "ref = %d\n", ref); +} + static void test_typeinfo(const WCHAR *parse_func_name) { static struct @@ -1043,7 +1613,7 @@ static void test_typeinfo(const WCHAR *parse_func_name) else { parse_script(parser, source); - disp = get_script_dispatch(script); + disp = get_script_dispatch(script, NULL); }
hr = IDispatchEx_QueryInterface(disp, &IID_ITypeInfo, (void**)&typeinfo); @@ -1406,6 +1976,7 @@ START_TEST(jscript) test_jscript_uninitializing(); test_aggregation(); test_code_persistence(); + test_named_items(); test_typeinfo(NULL); test_typeinfo(L"some_func_name");