Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Will be needed to pass the tests at the end of the series.
dlls/jscript/dispex.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index da23d18..3c27330 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -1083,6 +1083,7 @@ HRESULT jsdisp_get_id(jsdisp_t *jsdisp, const WCHAR *name, DWORD flags, DISPID * }
TRACE("not found %s\n", debugstr_w(name)); + *id = DISPID_UNKNOWN; return DISP_E_UNKNOWNNAME; }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- 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 75eef39..2a7c028 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -231,7 +231,7 @@ static void decrease_state(JScript *This, SCRIPTSTATE state) FIXME("NULL ctx\n"); }
- if(state == SCRIPTSTATE_UNINITIALIZED) + if(state == SCRIPTSTATE_UNINITIALIZED || state == SCRIPTSTATE_CLOSED) This->thread_id = 0;
if(This->site) {
Signed-off-by: Jacek Caban jacek@codeweavers.com
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 | 47 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+)
diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index a3b598e..c7ea75a 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -186,6 +186,7 @@ typedef struct _bytecode_t { unsigned str_cnt;
struct _bytecode_t *next; + struct _bytecode_t *persistent_next; } 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..b059a31 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -62,6 +62,8 @@ typedef struct {
bytecode_t *queue_head; bytecode_t *queue_tail; + bytecode_t *persistent_head; + bytecode_t *persistent_tail; } JScript;
void script_release(script_ctx_t *ctx) @@ -133,6 +135,40 @@ static void clear_script_queue(JScript *This) This->queue_head = This->queue_tail = NULL; }
+static void clear_persistent_scripts(JScript *This) +{ + bytecode_t *iter, *iter2; + + if(!This->persistent_head) + return; + + iter = This->persistent_head; + while(iter) { + iter2 = iter->persistent_next; + iter->persistent_next = NULL; + release_bytecode(iter); + iter = iter2; + } + + This->persistent_head = This->persistent_tail = NULL; +} + +static void queue_persistent_scripts(JScript *This) +{ + bytecode_t *iter; + + if(!This->persistent_head) + return; + + iter = This->queue_head = This->persistent_head; + This->queue_tail = This->persistent_tail; + while(iter) { + iter->next = iter->persistent_next; + bytecode_addref(iter); + iter = iter->next; + } +} + static void exec_queued_code(JScript *This) { bytecode_t *iter; @@ -472,6 +508,7 @@ static HRESULT WINAPI JScript_SetScriptState(IActiveScript *iface, SCRIPTSTATE s return E_UNEXPECTED;
decrease_state(This, SCRIPTSTATE_UNINITIALIZED); + queue_persistent_scripts(This); return S_OK; }
@@ -524,6 +561,8 @@ static HRESULT WINAPI JScript_Close(IActiveScript *iface) return E_UNEXPECTED;
decrease_state(This, SCRIPTSTATE_CLOSED); + clear_script_queue(This); + clear_persistent_scripts(This); return S_OK; }
@@ -788,6 +827,14 @@ static HRESULT WINAPI JScriptParse_ParseScriptText(IActiveScriptParse *iface, return hres; }
+ if(dwFlags & SCRIPTTEXT_ISPERSISTENT) { + if(This->persistent_tail) + This->persistent_tail = This->persistent_tail->persistent_next = code; + else + This->persistent_head = This->persistent_tail = code; + bytecode_addref(code); + } + /* * 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.
Hi Gabriel,
On 11/6/19 4:28 PM, Gabriel Ivăncescu wrote:
--- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -62,6 +62,8 @@ typedef struct {
bytecode_t *queue_head; bytecode_t *queue_tail;
- bytecode_t *persistent_head;
- bytecode_t *persistent_tail;
I think it would be cleaner to use struct list from wine/list.h instead.
Thanks,
Jacek
On 11/6/19 5:50 PM, Jacek Caban wrote:
Hi Gabriel,
On 11/6/19 4:28 PM, Gabriel Ivăncescu wrote:
--- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -62,6 +62,8 @@ typedef struct { bytecode_t *queue_head; bytecode_t *queue_tail; + bytecode_t *persistent_head; + bytecode_t *persistent_tail;
I think it would be cleaner to use struct list from wine/list.h instead.
I replied too soon. It would be generally preferable, more importantly, it's the same question as I had for vbscript: why do we need a separated list? Can we just store a flag?
Thanks,
Jacek
On 11/6/19 6:57 PM, Jacek Caban wrote:
On 11/6/19 5:50 PM, Jacek Caban wrote:
Hi Gabriel,
On 11/6/19 4:28 PM, Gabriel Ivăncescu wrote:
--- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -62,6 +62,8 @@ typedef struct { bytecode_t *queue_head; bytecode_t *queue_tail; + bytecode_t *persistent_head; + bytecode_t *persistent_tail;
I think it would be cleaner to use struct list from wine/list.h instead.
I replied too soon. It would be generally preferable, more importantly, it's the same question as I had for vbscript: why do we need a separated list? Can we just store a flag?
Thanks,
Jacek
We can use a flag, but then we have to also use a flag for queued code, just like vbscript (where it has both pending_exec and is_persistent). Meaning we have to revamp the current implementation with queue list.
That's because without a pending_exec flag, we'll lose the code list when the queue gets cleared -- and thus, access to the persistent code list, too. Unless, of course, we store two lists but that's what the patch had already.
The reason is that persistent and non-persistent code can be mixed in ordering, for example (P = persistent, N = non-persistent):
P->P->N->P->N
So the queue now looks like the above. Then the script is started and they get executed.
When it is uninitialized, and then re-initialized, the queue is now:
P->P->P
because the persistent code has to be re-executed. Adding more code before starting the script will add to the end, of course.
But currently, we keep a queue of pending code *only*. So, if the script gets started, the queue gets cleared. That means we lose access to the persistent code list without a second list, and thus can't re-queue it back when it is uninitialized.
So, should I proceed with two flags like in vbscript and a single list?
Thanks, Gabriel
On 11/7/19 1:19 PM, Gabriel Ivăncescu wrote:
On 11/6/19 6:57 PM, Jacek Caban wrote:
On 11/6/19 5:50 PM, Jacek Caban wrote:
Hi Gabriel,
On 11/6/19 4:28 PM, Gabriel Ivăncescu wrote:
--- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -62,6 +62,8 @@ typedef struct { bytecode_t *queue_head; bytecode_t *queue_tail; + bytecode_t *persistent_head; + bytecode_t *persistent_tail;
I think it would be cleaner to use struct list from wine/list.h instead.
I replied too soon. It would be generally preferable, more importantly, it's the same question as I had for vbscript: why do we need a separated list? Can we just store a flag?
Thanks,
Jacek
We can use a flag, but then we have to also use a flag for queued code, just like vbscript (where it has both pending_exec and is_persistent). Meaning we have to revamp the current implementation with queue list.
That's because without a pending_exec flag, we'll lose the code list when the queue gets cleared -- and thus, access to the persistent code list, too. Unless, of course, we store two lists but that's what the patch had already.
The reason is that persistent and non-persistent code can be mixed in ordering, for example (P = persistent, N = non-persistent):
P->P->N->P->N
So the queue now looks like the above. Then the script is started and they get executed.
When it is uninitialized, and then re-initialized, the queue is now:
P->P->P
because the persistent code has to be re-executed. Adding more code before starting the script will add to the end, of course.
But currently, we keep a queue of pending code *only*. So, if the script gets started, the queue gets cleared. That means we lose access to the persistent code list without a second list, and thus can't re-queue it back when it is uninitialized.
So, should I proceed with two flags like in vbscript and a single list?
I can see your point. I'm not against separated lists, but I don't really like the way your patch makes it more complicated than it needs to be, eg. by adding additional fields to bytecode_t. I think that if you'd have the bytecode in only one list at a time (so pending persistent code would be only on pending list and it would be moved to persistent list when it's executed), it could be a bit cleaner. Of course then you'd need to store persistent flags to know which pending code should be removed when changing the state to SCRIPTSTATE_UNINITIALIZED.
In any case, to be able to easily remove entries from a queue, a double linked list would be nicer, so changing bytecode_t to use standard list first seems like a good idea.
Thanks,
Jacek
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;