Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/engine.h | 2 +- dlls/jscript/jscript.c | 26 ++++++++------------------ 2 files changed, 9 insertions(+), 19 deletions(-)
diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index a3b598e..1d76cf8 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -185,7 +185,7 @@ typedef struct _bytecode_t { unsigned str_pool_size; unsigned str_cnt;
- struct _bytecode_t *next; + struct list entry; } bytecode_t;
HRESULT compile_script(script_ctx_t*,const WCHAR*,const WCHAR*,const WCHAR*,BOOL,BOOL,bytecode_t**) DECLSPEC_HIDDEN; diff --git a/dlls/jscript/jscript.c b/dlls/jscript/jscript.c index 2a7c028..68b62b4 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -60,8 +60,7 @@ typedef struct {
IActiveScriptSite *site;
- bytecode_t *queue_head; - bytecode_t *queue_tail; + struct list queued_code; } JScript;
void script_release(script_ctx_t *ctx) @@ -117,27 +116,20 @@ static HRESULT exec_global_code(JScript *This, bytecode_t *code)
static void clear_script_queue(JScript *This) { - bytecode_t *iter, *iter2; + bytecode_t *iter, *iter_next;
- if(!This->queue_head) - return; - - iter = This->queue_head; - while(iter) { - iter2 = iter->next; - iter->next = NULL; + LIST_FOR_EACH_ENTRY_SAFE(iter, iter_next, &This->queued_code, bytecode_t, entry) + { + list_remove(&iter->entry); release_bytecode(iter); - iter = iter2; } - - This->queue_head = This->queue_tail = NULL; }
static void exec_queued_code(JScript *This) { bytecode_t *iter;
- for(iter = This->queue_head; iter; iter = iter->next) + LIST_FOR_EACH_ENTRY(iter, &This->queued_code, bytecode_t, entry) exec_global_code(This, iter);
clear_script_queue(This); @@ -793,10 +785,7 @@ static HRESULT WINAPI JScriptParse_ParseScriptText(IActiveScriptParse *iface, * script is executed immediately, even if it's not in started state yet. */ if(!pvarResult && !is_started(This->ctx)) { - if(This->queue_tail) - This->queue_tail = This->queue_tail->next = code; - else - This->queue_head = This->queue_tail = code; + list_add_tail(&This->queued_code, &code->entry); return S_OK; }
@@ -1089,6 +1078,7 @@ HRESULT create_jscript_object(BOOL is_encode, REFIID riid, void **ppv) ret->ref = 1; ret->safeopt = INTERFACE_USES_DISPEX; ret->is_encode = is_encode; + list_init(&ret->queued_code);
hres = IActiveScript_QueryInterface(&ret->IActiveScript_iface, riid, ppv); IActiveScript_Release(&ret->IActiveScript_iface);
Persistent code has to be re-executed if the script is uninitialized and then reinitialized and restarted.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/engine.h | 1 + dlls/jscript/jscript.c | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index 1d76cf8..955c315 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -169,6 +169,7 @@ local_ref_t *lookup_local(const function_code_t*,const WCHAR*) DECLSPEC_HIDDEN;
typedef struct _bytecode_t { LONG ref; + BOOL is_persistent;
instr_t *instrs; heap_pool_t heap; diff --git a/dlls/jscript/jscript.c b/dlls/jscript/jscript.c index 68b62b4..57c3e61 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -60,6 +60,7 @@ typedef struct {
IActiveScriptSite *site;
+ struct list persistent_code; struct list queued_code; } JScript;
@@ -119,12 +120,37 @@ static void clear_script_queue(JScript *This) bytecode_t *iter, *iter_next;
LIST_FOR_EACH_ENTRY_SAFE(iter, iter_next, &This->queued_code, bytecode_t, entry) + { + list_remove(&iter->entry); + if (iter->is_persistent) + list_add_tail(&This->persistent_code, &iter->entry); + else + release_bytecode(iter); + } +} + +static void clear_persistent_code_list(JScript *This) +{ + bytecode_t *iter, *iter_next; + + LIST_FOR_EACH_ENTRY_SAFE(iter, iter_next, &This->persistent_code, bytecode_t, entry) { list_remove(&iter->entry); release_bytecode(iter); } }
+static void queue_persistent_code(JScript *This) +{ + bytecode_t *iter, *iter_next; + + LIST_FOR_EACH_ENTRY_SAFE(iter, iter_next, &This->persistent_code, bytecode_t, entry) + { + list_remove(&iter->entry); + list_add_tail(&This->queued_code, &iter->entry); + } +} + static void exec_queued_code(JScript *This) { bytecode_t *iter; @@ -210,6 +236,7 @@ static void decrease_state(JScript *This, SCRIPTSTATE state) /* FALLTHROUGH */ case SCRIPTSTATE_UNINITIALIZED: change_state(This, state); + clear_script_queue(This); break; default: assert(0); @@ -464,6 +491,7 @@ static HRESULT WINAPI JScript_SetScriptState(IActiveScript *iface, SCRIPTSTATE s return E_UNEXPECTED;
decrease_state(This, SCRIPTSTATE_UNINITIALIZED); + queue_persistent_code(This); return S_OK; }
@@ -516,6 +544,7 @@ static HRESULT WINAPI JScript_Close(IActiveScript *iface) return E_UNEXPECTED;
decrease_state(This, SCRIPTSTATE_CLOSED); + clear_persistent_code_list(This); return S_OK; }
@@ -780,6 +809,8 @@ static HRESULT WINAPI JScriptParse_ParseScriptText(IActiveScriptParse *iface, return hres; }
+ code->is_persistent = (dwFlags & SCRIPTTEXT_ISPERSISTENT) != 0; + /* * Although pvarResult is not really used without SCRIPTTEXT_ISEXPRESSION flag, if it's not NULL, * script is executed immediately, even if it's not in started state yet. @@ -790,7 +821,12 @@ static HRESULT WINAPI JScriptParse_ParseScriptText(IActiveScriptParse *iface, }
hres = exec_global_code(This, code); - release_bytecode(code); + + if(code->is_persistent) + list_add_tail(&This->persistent_code, &code->entry); + else + release_bytecode(code); + if(FAILED(hres)) return hres;
@@ -1078,6 +1114,7 @@ HRESULT create_jscript_object(BOOL is_encode, REFIID riid, void **ppv) ret->ref = 1; ret->safeopt = INTERFACE_USES_DISPEX; ret->is_encode = is_encode; + list_init(&ret->persistent_code); list_init(&ret->queued_code);
hres = IActiveScript_QueryInterface(&ret->IActiveScript_iface, riid, ppv);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This will be needed for next patch, since we'll need to record two such calls at one point.
dlls/jscript/tests/jscript.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/dlls/jscript/tests/jscript.c b/dlls/jscript/tests/jscript.c index 4c5aa6c..b97e823 100644 --- a/dlls/jscript/tests/jscript.c +++ b/dlls/jscript/tests/jscript.c @@ -51,27 +51,27 @@ static const CLSID CLSID_JScriptEncode = {0xf414c262,0x6ac0,0x11cf,{0xb6,0xd1,0x00,0xaa,0x00,0xbb,0xbb,0x58}};
#define DEFINE_EXPECT(func) \ - static BOOL expect_ ## func = FALSE, called_ ## func = FALSE + static int expect_ ## func = 0, called_ ## func = 0
#define SET_EXPECT(func) \ - expect_ ## func = TRUE + expect_ ## func = 1
#define CHECK_EXPECT2(func) \ do { \ ok(expect_ ##func, "unexpected call " #func "\n"); \ - called_ ## func = TRUE; \ + called_ ## func++; \ }while(0)
#define CHECK_EXPECT(func) \ do { \ CHECK_EXPECT2(func); \ - expect_ ## func = FALSE; \ + expect_ ## func--; \ }while(0)
#define CHECK_CALLED(func) \ do { \ ok(called_ ## func, "expected " #func "\n"); \ - expect_ ## func = called_ ## func = FALSE; \ + expect_ ## func = called_ ## func = 0; \ }while(0)
DEFINE_EXPECT(GetLCID);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/tests/jscript.c | 269 +++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+)
diff --git a/dlls/jscript/tests/jscript.c b/dlls/jscript/tests/jscript.c index b97e823..5742027 100644 --- a/dlls/jscript/tests/jscript.c +++ b/dlls/jscript/tests/jscript.c @@ -56,6 +56,9 @@ static const CLSID CLSID_JScriptEncode = #define SET_EXPECT(func) \ expect_ ## func = 1
+#define SET_EXPECT_MULTI(func, num) \ + expect_ ## func = num + #define CHECK_EXPECT2(func) \ do { \ ok(expect_ ##func, "unexpected call " #func "\n"); \ @@ -74,6 +77,12 @@ static const CLSID CLSID_JScriptEncode = expect_ ## func = called_ ## func = 0; \ }while(0)
+#define CHECK_CALLED_MULTI(func, num) \ + do { \ + ok(called_ ## func == num, "expected " #func " %d times (got %d)\n", num, called_ ## func); \ + expect_ ## func = called_ ## func = 0; \ + }while(0) + DEFINE_EXPECT(GetLCID); DEFINE_EXPECT(OnStateChange_UNINITIALIZED); DEFINE_EXPECT(OnStateChange_STARTED); @@ -277,6 +286,23 @@ static IDispatchEx *get_script_dispatch(IActiveScript *script) return dispex; }
+#define get_disp_id(a,b,c,d) _get_disp_id(__LINE__,a,b,c,d) +static void _get_disp_id(unsigned line, IDispatchEx *dispex, const char *name, HRESULT exhr, DISPID *id) +{ + DISPID id2; + HRESULT hr; + BSTR str; + + str = a2bstr(name); + hr = IDispatchEx_GetDispID(dispex, str, 0, id); + ok_(__FILE__,line)(hr == exhr, "GetDispID(%s) returned %08x, expected %08x\n", name, hr, exhr); + + hr = IDispatchEx_GetIDsOfNames(dispex, &IID_NULL, &str, 1, 0, &id2); + SysFreeString(str); + ok_(__FILE__,line)(hr == exhr, "GetIDsOfNames(%s) returned %08x, expected %08x\n", name, hr, exhr); + ok_(__FILE__,line)(*id == id2, "GetIDsOfNames(%s) id != id2\n", name); +} + static void test_no_script_dispatch(IActiveScript *script) { IDispatch *disp; @@ -664,6 +690,248 @@ static void test_aggregation(void) ok(!unk || broken(unk != NULL), "unk = %p\n", unk); }
+static void test_code_persistence(void) +{ + IActiveScriptParse *parse; + IActiveScript *script; + IDispatchEx *dispex; + VARIANT var; + HRESULT hr; + DISPID id; + ULONG ref; + BSTR str; + + script = create_jscript(); + + hr = IActiveScript_QueryInterface(script, &IID_IActiveScriptParse, (void**)&parse); + ok(hr == S_OK, "Could not get IActiveScriptParse iface: %08x\n", hr); + test_state(script, SCRIPTSTATE_UNINITIALIZED); + test_safety((IUnknown*)script); + + SET_EXPECT(GetLCID); + hr = IActiveScript_SetScriptSite(script, &ActiveScriptSite); + ok(hr == S_OK, "SetScriptSite failed: %08x\n", hr); + CHECK_CALLED(GetLCID); + + SET_EXPECT(OnStateChange_INITIALIZED); + hr = IActiveScriptParse_InitNew(parse); + ok(hr == S_OK, "InitNew failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_INITIALIZED); + test_state(script, SCRIPTSTATE_INITIALIZED); + + str = a2bstr( + "var x = 1;\n" + "var y = 2;\n"); + hr = IActiveScriptParse_ParseScriptText(parse, str, NULL, NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + SysFreeString(str); + + str = a2bstr( + "var z = 3;\n" + "var y = 42;\n" + "var v = 10;\n"); + hr = IActiveScriptParse_ParseScriptText(parse, str, NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISPERSISTENT, NULL, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + SysFreeString(str); + + /* Pending code does not add identifiers to the global scope */ + dispex = get_script_dispatch(script); + id = 0; + get_disp_id(dispex, "x", DISP_E_UNKNOWNNAME, &id); + ok(id == -1, "id = %d, expected -1\n", id); + id = 0; + get_disp_id(dispex, "y", DISP_E_UNKNOWNNAME, &id); + ok(id == -1, "id = %d, expected -1\n", id); + id = 0; + get_disp_id(dispex, "z", DISP_E_UNKNOWNNAME, &id); + ok(id == -1, "id = %d, expected -1\n", id); + IDispatchEx_Release(dispex); + + /* Uninitialized state removes code without SCRIPTTEXT_ISPERSISTENT */ + SET_EXPECT(OnStateChange_UNINITIALIZED); + hr = IActiveScript_SetScriptState(script, SCRIPTSTATE_UNINITIALIZED); + ok(hr == S_OK, "SetScriptState(SCRIPTSTATE_UNINITIALIZED) failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_UNINITIALIZED); + test_no_script_dispatch(script); + + 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); + + str = a2bstr("v = 20;\n"); + hr = IActiveScriptParse_ParseScriptText(parse, str, NULL, NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + SysFreeString(str); + + 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); + id = 0; + get_disp_id(dispex, "x", DISP_E_UNKNOWNNAME, &id); + ok(id == -1, "id = %d, expected -1\n", id); + id = 0; + get_disp_id(dispex, "y", S_OK, &id); + ok(id != -1, "id = -1\n"); + id = 0; + get_disp_id(dispex, "z", S_OK, &id); + ok(id != -1, "id = -1\n"); + IDispatchEx_Release(dispex); + + str = a2bstr("y"); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, str, NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &var, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + ok(V_VT(&var) == VT_I4 && V_I2(&var) == 42, "V_VT(y) = %d, V_I2(y) = %d\n", V_VT(&var), V_I2(&var)); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + SysFreeString(str); + + str = a2bstr("v"); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, str, NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &var, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + ok(V_VT(&var) == VT_I4 && V_I2(&var) == 20, "V_VT(var) = %d, V_I2(var) = %d\n", V_VT(&var), V_I2(&var)); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + SysFreeString(str); + + /* Uninitialized state does not remove persistent code, even if it was executed */ + 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); + + 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); + + dispex = get_script_dispatch(script); + id = 0; + get_disp_id(dispex, "z", DISP_E_UNKNOWNNAME, &id); + ok(id == -1, "id = %d, expected -1\n", id); + IDispatchEx_Release(dispex); + + SET_EXPECT(OnStateChange_CONNECTED); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScript_SetScriptState(script, SCRIPTSTATE_CONNECTED); + ok(hr == S_OK, "SetScriptState(SCRIPTSTATE_CONNECTED) failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_CONNECTED); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + test_state(script, SCRIPTSTATE_CONNECTED); + + dispex = get_script_dispatch(script); + id = 0; + get_disp_id(dispex, "z", S_OK, &id); + ok(id != -1, "id = -1\n"); + IDispatchEx_Release(dispex); + + str = a2bstr("y"); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, str, NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &var, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + ok(V_VT(&var) == VT_I4 && V_I2(&var) == 42, "V_VT(y) = %d, V_I2(y) = %d\n", V_VT(&var), V_I2(&var)); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + SysFreeString(str); + + str = a2bstr("v"); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parse, str, NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &var, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + ok(V_VT(&var) == VT_I4 && V_I2(&var) == 10, "V_VT(var) = %d, V_I2(var) = %d\n", V_VT(&var), V_I2(&var)); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + SysFreeString(str); + + 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); + + 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); + + str = a2bstr("y = 2;\n"); + hr = IActiveScriptParse_ParseScriptText(parse, str, NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISPERSISTENT, NULL, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + SysFreeString(str); + + /* Closing the script engine removes all code (even if it's pending and persistent) */ + SET_EXPECT(OnStateChange_CLOSED); + hr = IActiveScript_Close(script); + ok(hr == S_OK, "Close failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_CLOSED); + test_state(script, SCRIPTSTATE_CLOSED); + test_no_script_dispatch(script); + + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(GetLCID); + hr = IActiveScript_SetScriptSite(script, &ActiveScriptSite); + ok(hr == S_OK, "SetScriptSite failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(GetLCID); + test_state(script, SCRIPTSTATE_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); + test_state(script, SCRIPTSTATE_CONNECTED); + + dispex = get_script_dispatch(script); + id = 0; + get_disp_id(dispex, "y", DISP_E_UNKNOWNNAME, &id); + ok(id == -1, "id = %d, expected -1\n", id); + id = 0; + get_disp_id(dispex, "z", DISP_E_UNKNOWNNAME, &id); + ok(id == -1, "id = %d, expected -1\n", id); + IDispatchEx_Release(dispex); + + IActiveScriptParse_Release(parse); + + SET_EXPECT(OnStateChange_DISCONNECTED); + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(OnStateChange_CLOSED); + ref = IActiveScript_Release(script); + ok(!ref, "ref = %d\n", ref); + CHECK_CALLED(OnStateChange_DISCONNECTED); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(OnStateChange_CLOSED); +} + static BOOL check_jscript(void) { IActiveScriptProperty *script_prop; @@ -687,6 +955,7 @@ START_TEST(jscript) test_jscript2(); test_jscript_uninitializing(); test_aggregation(); + test_code_persistence();
trace("Testing JScriptEncode object...\n"); engine_clsid = &CLSID_JScriptEncode;