vbscript: Added beginning parser implementation.
[wine] / dlls / vbscript / vbscript.c
1 /*
2  * Copyright 2011 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19
20 #include <assert.h>
21
22 #include "vbscript.h"
23 #include "parse.h"
24
25 #include "objsafe.h"
26
27 #include "wine/debug.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(vbscript);
30
31 #ifdef _WIN64
32
33 #define CTXARG_T DWORDLONG
34 #define IActiveScriptParseVtbl IActiveScriptParse64Vtbl
35
36 #else
37
38 #define CTXARG_T DWORD
39 #define IActiveScriptParseVtbl IActiveScriptParse32Vtbl
40
41 #endif
42
43 struct VBScript {
44     IActiveScript IActiveScript_iface;
45     IActiveScriptParse IActiveScriptParse_iface;
46     IObjectSafety IObjectSafety_iface;
47
48     LONG ref;
49
50     DWORD safeopt;
51     SCRIPTSTATE state;
52     IActiveScriptSite *site;
53     script_ctx_t *ctx;
54     LONG thread_id;
55     LCID lcid;
56 };
57
58 static void change_state(VBScript *This, SCRIPTSTATE state)
59 {
60     if(This->state == state)
61         return;
62
63     This->state = state;
64     if(This->site)
65         IActiveScriptSite_OnStateChange(This->site, state);
66 }
67
68 static void exec_queued_code(VBScript *This)
69 {
70     FIXME("\n");
71 }
72
73 static HRESULT set_ctx_site(VBScript *This)
74 {
75     HRESULT hres;
76
77     This->ctx->lcid = This->lcid;
78
79     hres = init_global(This->ctx);
80     if(FAILED(hres))
81         return hres;
82
83     IActiveScriptSite_AddRef(This->site);
84     This->ctx->site = This->site;
85
86     change_state(This, SCRIPTSTATE_INITIALIZED);
87     return S_OK;
88 }
89
90 static void destroy_script(script_ctx_t *ctx)
91 {
92     while(!list_empty(&ctx->named_items)) {
93         named_item_t *iter = LIST_ENTRY(list_head(&ctx->named_items), named_item_t, entry);
94
95         list_remove(&iter->entry);
96         if(iter->disp)
97             IDispatch_Release(iter->disp);
98         heap_free(iter->name);
99         heap_free(iter);
100     }
101
102     if(ctx->host_global)
103         IDispatch_Release(ctx->host_global);
104     if(ctx->site)
105         IActiveScriptSite_Release(ctx->site);
106     if(ctx->script_obj)
107         IDispatchEx_Release(&ctx->script_obj->IDispatchEx_iface);
108     heap_free(ctx);
109 }
110
111 static void decrease_state(VBScript *This, SCRIPTSTATE state)
112 {
113     switch(This->state) {
114     case SCRIPTSTATE_CONNECTED:
115         change_state(This, SCRIPTSTATE_DISCONNECTED);
116         if(state == SCRIPTSTATE_DISCONNECTED)
117             return;
118         /* FALLTHROUGH */
119     case SCRIPTSTATE_STARTED:
120     case SCRIPTSTATE_DISCONNECTED:
121         if(This->state == SCRIPTSTATE_DISCONNECTED)
122             change_state(This, SCRIPTSTATE_INITIALIZED);
123         if(state == SCRIPTSTATE_INITIALIZED)
124             break;
125         /* FALLTHROUGH */
126     case SCRIPTSTATE_INITIALIZED:
127     case SCRIPTSTATE_UNINITIALIZED:
128         change_state(This, state);
129
130         if(This->site) {
131             IActiveScriptSite_Release(This->site);
132             This->site = NULL;
133         }
134
135         This->thread_id = 0;
136
137         if(state == SCRIPTSTATE_CLOSED) {
138             destroy_script(This->ctx);
139             This->ctx = NULL;
140         }
141
142         break;
143     default:
144         assert(0);
145     }
146 }
147
148 static inline VBScript *impl_from_IActiveScript(IActiveScript *iface)
149 {
150     return CONTAINING_RECORD(iface, VBScript, IActiveScript_iface);
151 }
152
153 static HRESULT WINAPI VBScript_QueryInterface(IActiveScript *iface, REFIID riid, void **ppv)
154 {
155     VBScript *This = impl_from_IActiveScript(iface);
156
157     if(IsEqualGUID(riid, &IID_IUnknown)) {
158         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
159         *ppv = &This->IActiveScript_iface;
160     }else if(IsEqualGUID(riid, &IID_IActiveScript)) {
161         TRACE("(%p)->(IID_IActiveScript %p)\n", This, ppv);
162         *ppv = &This->IActiveScript_iface;
163     }else if(IsEqualGUID(riid, &IID_IActiveScriptParse)) {
164         TRACE("(%p)->(IID_IActiveScriptParse %p)\n", This, ppv);
165         *ppv = &This->IActiveScriptParse_iface;
166     }else if(IsEqualGUID(riid, &IID_IObjectSafety)) {
167         TRACE("(%p)->(IID_IObjectSafety %p)\n", This, ppv);
168         *ppv = &This->IObjectSafety_iface;
169     }else {
170         FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
171         *ppv = NULL;
172         return E_NOINTERFACE;
173     }
174
175     IUnknown_AddRef((IUnknown*)*ppv);
176     return S_OK;
177 }
178
179 static ULONG WINAPI VBScript_AddRef(IActiveScript *iface)
180 {
181     VBScript *This = impl_from_IActiveScript(iface);
182     LONG ref = InterlockedIncrement(&This->ref);
183
184     TRACE("(%p) ref=%d\n", This, ref);
185
186     return ref;
187 }
188
189 static ULONG WINAPI VBScript_Release(IActiveScript *iface)
190 {
191     VBScript *This = impl_from_IActiveScript(iface);
192     LONG ref = InterlockedDecrement(&This->ref);
193
194     TRACE("(%p) ref=%d\n", iface, ref);
195
196     if(!ref) {
197         if(This->site)
198             IActiveScriptSite_Release(This->site);
199         heap_free(This);
200     }
201
202     return ref;
203 }
204
205 static HRESULT WINAPI VBScript_SetScriptSite(IActiveScript *iface, IActiveScriptSite *pass)
206 {
207     VBScript *This = impl_from_IActiveScript(iface);
208     LCID lcid;
209     HRESULT hres;
210
211     TRACE("(%p)->(%p)\n", This, pass);
212
213     if(!pass)
214         return E_POINTER;
215
216     if(This->site)
217         return E_UNEXPECTED;
218
219     if(InterlockedCompareExchange(&This->thread_id, GetCurrentThreadId(), 0))
220         return E_UNEXPECTED;
221
222     This->site = pass;
223     IActiveScriptSite_AddRef(This->site);
224
225     hres = IActiveScriptSite_GetLCID(This->site, &lcid);
226     if(hres == S_OK)
227         This->lcid = lcid;
228
229     return This->ctx ? set_ctx_site(This) : S_OK;
230 }
231
232 static HRESULT WINAPI VBScript_GetScriptSite(IActiveScript *iface, REFIID riid,
233                                             void **ppvObject)
234 {
235     VBScript *This = impl_from_IActiveScript(iface);
236     FIXME("(%p)->()\n", This);
237     return E_NOTIMPL;
238 }
239
240 static HRESULT WINAPI VBScript_SetScriptState(IActiveScript *iface, SCRIPTSTATE ss)
241 {
242     VBScript *This = impl_from_IActiveScript(iface);
243
244     TRACE("(%p)->(%d)\n", This, ss);
245
246     if(This->thread_id && GetCurrentThreadId() != This->thread_id)
247         return E_UNEXPECTED;
248
249     if(ss == SCRIPTSTATE_UNINITIALIZED) {
250         if(This->state == SCRIPTSTATE_CLOSED)
251             return E_UNEXPECTED;
252
253         decrease_state(This, SCRIPTSTATE_UNINITIALIZED);
254         return S_OK;
255     }
256
257     if(!This->ctx)
258         return E_UNEXPECTED;
259
260     switch(ss) {
261     case SCRIPTSTATE_STARTED:
262     case SCRIPTSTATE_CONNECTED: /* FIXME */
263         if(This->state == SCRIPTSTATE_CLOSED)
264             return E_UNEXPECTED;
265
266         exec_queued_code(This);
267         break;
268     case SCRIPTSTATE_INITIALIZED:
269         FIXME("unimplemented SCRIPTSTATE_INITIALIZED\n");
270         return S_OK;
271     default:
272         FIXME("unimplemented state %d\n", ss);
273         return E_NOTIMPL;
274     }
275
276     change_state(This, ss);
277     return S_OK;
278 }
279
280 static HRESULT WINAPI VBScript_GetScriptState(IActiveScript *iface, SCRIPTSTATE *pssState)
281 {
282     VBScript *This = impl_from_IActiveScript(iface);
283
284     TRACE("(%p)->(%p)\n", This, pssState);
285
286     if(!pssState)
287         return E_POINTER;
288
289     if(This->thread_id && This->thread_id != GetCurrentThreadId())
290         return E_UNEXPECTED;
291
292     *pssState = This->state;
293     return S_OK;
294 }
295
296 static HRESULT WINAPI VBScript_Close(IActiveScript *iface)
297 {
298     VBScript *This = impl_from_IActiveScript(iface);
299
300     TRACE("(%p)->()\n", This);
301
302     if(This->thread_id && This->thread_id != GetCurrentThreadId())
303         return E_UNEXPECTED;
304
305     decrease_state(This, SCRIPTSTATE_CLOSED);
306     return S_OK;
307 }
308
309 static HRESULT WINAPI VBScript_AddNamedItem(IActiveScript *iface, LPCOLESTR pstrName, DWORD dwFlags)
310 {
311     VBScript *This = impl_from_IActiveScript(iface);
312     named_item_t *item;
313     IDispatch *disp = NULL;
314     HRESULT hres;
315
316     TRACE("(%p)->(%s %x)\n", This, debugstr_w(pstrName), dwFlags);
317
318     if(This->thread_id != GetCurrentThreadId() || !This->ctx || This->state == SCRIPTSTATE_CLOSED)
319         return E_UNEXPECTED;
320
321     if(dwFlags & SCRIPTITEM_GLOBALMEMBERS) {
322         IUnknown *unk;
323
324         hres = IActiveScriptSite_GetItemInfo(This->site, pstrName, SCRIPTINFO_IUNKNOWN, &unk, NULL);
325         if(FAILED(hres)) {
326             WARN("GetItemInfo failed: %08x\n", hres);
327             return hres;
328         }
329
330         hres = IUnknown_QueryInterface(unk, &IID_IDispatch, (void**)&disp);
331         IUnknown_Release(unk);
332         if(FAILED(hres)) {
333             WARN("object does not implement IDispatch\n");
334             return hres;
335         }
336
337         if(This->ctx->host_global)
338             IDispatch_Release(This->ctx->host_global);
339         IDispatch_AddRef(disp);
340         This->ctx->host_global = disp;
341     }
342
343     item = heap_alloc(sizeof(*item));
344     if(!item) {
345         if(disp)
346             IDispatch_Release(disp);
347         return E_OUTOFMEMORY;
348     }
349
350     item->disp = disp;
351     item->flags = dwFlags;
352     item->name = heap_strdupW(pstrName);
353     if(!item->name) {
354         if(disp)
355             IDispatch_Release(disp);
356         heap_free(item);
357         return E_OUTOFMEMORY;
358     }
359
360     list_add_tail(&This->ctx->named_items, &item->entry);
361     return S_OK;
362 }
363
364 static HRESULT WINAPI VBScript_AddTypeLib(IActiveScript *iface, REFGUID rguidTypeLib,
365         DWORD dwMajor, DWORD dwMinor, DWORD dwFlags)
366 {
367     VBScript *This = impl_from_IActiveScript(iface);
368     FIXME("(%p)->()\n", This);
369     return E_NOTIMPL;
370 }
371
372 static HRESULT WINAPI VBScript_GetScriptDispatch(IActiveScript *iface, LPCOLESTR pstrItemName, IDispatch **ppdisp)
373 {
374     VBScript *This = impl_from_IActiveScript(iface);
375
376     TRACE("(%p)->(%p)\n", This, ppdisp);
377
378     if(!ppdisp)
379         return E_POINTER;
380
381     if(This->thread_id != GetCurrentThreadId() || !This->ctx->script_obj) {
382         *ppdisp = NULL;
383         return E_UNEXPECTED;
384     }
385
386     *ppdisp = (IDispatch*)&This->ctx->script_obj->IDispatchEx_iface;
387     IDispatch_AddRef(*ppdisp);
388     return S_OK;
389 }
390
391 static HRESULT WINAPI VBScript_GetCurrentScriptThreadID(IActiveScript *iface,
392                                                        SCRIPTTHREADID *pstridThread)
393 {
394     VBScript *This = impl_from_IActiveScript(iface);
395     FIXME("(%p)->()\n", This);
396     return E_NOTIMPL;
397 }
398
399 static HRESULT WINAPI VBScript_GetScriptThreadID(IActiveScript *iface,
400                                                 DWORD dwWin32ThreadId, SCRIPTTHREADID *pstidThread)
401 {
402     VBScript *This = impl_from_IActiveScript(iface);
403     FIXME("(%p)->()\n", This);
404     return E_NOTIMPL;
405 }
406
407 static HRESULT WINAPI VBScript_GetScriptThreadState(IActiveScript *iface,
408         SCRIPTTHREADID stidThread, SCRIPTTHREADSTATE *pstsState)
409 {
410     VBScript *This = impl_from_IActiveScript(iface);
411     FIXME("(%p)->()\n", This);
412     return E_NOTIMPL;
413 }
414
415 static HRESULT WINAPI VBScript_InterruptScriptThread(IActiveScript *iface,
416         SCRIPTTHREADID stidThread, const EXCEPINFO *pexcepinfo, DWORD dwFlags)
417 {
418     VBScript *This = impl_from_IActiveScript(iface);
419     FIXME("(%p)->()\n", This);
420     return E_NOTIMPL;
421 }
422
423 static HRESULT WINAPI VBScript_Clone(IActiveScript *iface, IActiveScript **ppscript)
424 {
425     VBScript *This = impl_from_IActiveScript(iface);
426     FIXME("(%p)->()\n", This);
427     return E_NOTIMPL;
428 }
429
430 static const IActiveScriptVtbl VBScriptVtbl = {
431     VBScript_QueryInterface,
432     VBScript_AddRef,
433     VBScript_Release,
434     VBScript_SetScriptSite,
435     VBScript_GetScriptSite,
436     VBScript_SetScriptState,
437     VBScript_GetScriptState,
438     VBScript_Close,
439     VBScript_AddNamedItem,
440     VBScript_AddTypeLib,
441     VBScript_GetScriptDispatch,
442     VBScript_GetCurrentScriptThreadID,
443     VBScript_GetScriptThreadID,
444     VBScript_GetScriptThreadState,
445     VBScript_InterruptScriptThread,
446     VBScript_Clone
447 };
448
449 static inline VBScript *impl_from_IActiveScriptParse(IActiveScriptParse *iface)
450 {
451     return CONTAINING_RECORD(iface, VBScript, IActiveScriptParse_iface);
452 }
453
454 static HRESULT WINAPI VBScriptParse_QueryInterface(IActiveScriptParse *iface, REFIID riid, void **ppv)
455 {
456     VBScript *This = impl_from_IActiveScriptParse(iface);
457     return IActiveScript_QueryInterface(&This->IActiveScript_iface, riid, ppv);
458 }
459
460 static ULONG WINAPI VBScriptParse_AddRef(IActiveScriptParse *iface)
461 {
462     VBScript *This = impl_from_IActiveScriptParse(iface);
463     return IActiveScript_AddRef(&This->IActiveScript_iface);
464 }
465
466 static ULONG WINAPI VBScriptParse_Release(IActiveScriptParse *iface)
467 {
468     VBScript *This = impl_from_IActiveScriptParse(iface);
469     return IActiveScript_Release(&This->IActiveScript_iface);
470 }
471
472 static HRESULT WINAPI VBScriptParse_InitNew(IActiveScriptParse *iface)
473 {
474     VBScript *This = impl_from_IActiveScriptParse(iface);
475     script_ctx_t *ctx, *old_ctx;
476
477     TRACE("(%p)\n", This);
478
479     if(This->ctx)
480         return E_UNEXPECTED;
481
482     ctx = heap_alloc_zero(sizeof(script_ctx_t));
483     if(!ctx)
484         return E_OUTOFMEMORY;
485
486     list_init(&ctx->named_items);
487
488     old_ctx = InterlockedCompareExchangePointer((void**)&This->ctx, ctx, NULL);
489     if(old_ctx) {
490         destroy_script(ctx);
491         return E_UNEXPECTED;
492     }
493
494     return This->site ? set_ctx_site(This) : S_OK;
495 }
496
497 static HRESULT WINAPI VBScriptParse_AddScriptlet(IActiveScriptParse *iface,
498         LPCOLESTR pstrDefaultName, LPCOLESTR pstrCode, LPCOLESTR pstrItemName,
499         LPCOLESTR pstrSubItemName, LPCOLESTR pstrEventName, LPCOLESTR pstrDelimiter,
500         CTXARG_T dwSourceContextCookie, ULONG ulStartingLineNumber, DWORD dwFlags,
501         BSTR *pbstrName, EXCEPINFO *pexcepinfo)
502 {
503     VBScript *This = impl_from_IActiveScriptParse(iface);
504     FIXME("(%p)->(%s %s %s %s %s %s %s %u %x %p %p)\n", This, debugstr_w(pstrDefaultName),
505           debugstr_w(pstrCode), debugstr_w(pstrItemName), debugstr_w(pstrSubItemName),
506           debugstr_w(pstrEventName), debugstr_w(pstrDelimiter), wine_dbgstr_longlong(dwSourceContextCookie),
507           ulStartingLineNumber, dwFlags, pbstrName, pexcepinfo);
508     return E_NOTIMPL;
509 }
510
511 static HRESULT WINAPI VBScriptParse_ParseScriptText(IActiveScriptParse *iface,
512         LPCOLESTR pstrCode, LPCOLESTR pstrItemName, IUnknown *punkContext,
513         LPCOLESTR pstrDelimiter, CTXARG_T dwSourceContextCookie, ULONG ulStartingLine,
514         DWORD dwFlags, VARIANT *pvarResult, EXCEPINFO *pexcepinfo)
515 {
516     VBScript *This = impl_from_IActiveScriptParse(iface);
517     parser_ctx_t parser;
518     HRESULT hres;
519
520     TRACE("(%p)->(%s %s %p %s %s %u %x %p %p)\n", This, debugstr_w(pstrCode),
521           debugstr_w(pstrItemName), punkContext, debugstr_w(pstrDelimiter),
522           wine_dbgstr_longlong(dwSourceContextCookie), ulStartingLine, dwFlags, pvarResult, pexcepinfo);
523
524     hres = parse_script(&parser, pstrCode);
525     if(FAILED(hres))
526         return hres;
527
528     FIXME("compiling script not implemented\n");
529     return E_NOTIMPL;
530 }
531
532 static const IActiveScriptParseVtbl VBScriptParseVtbl = {
533     VBScriptParse_QueryInterface,
534     VBScriptParse_AddRef,
535     VBScriptParse_Release,
536     VBScriptParse_InitNew,
537     VBScriptParse_AddScriptlet,
538     VBScriptParse_ParseScriptText
539 };
540
541 static inline VBScript *impl_from_IObjectSafety(IObjectSafety *iface)
542 {
543     return CONTAINING_RECORD(iface, VBScript, IObjectSafety_iface);
544 }
545
546 static HRESULT WINAPI VBScriptSafety_QueryInterface(IObjectSafety *iface, REFIID riid, void **ppv)
547 {
548     VBScript *This = impl_from_IObjectSafety(iface);
549     return IActiveScript_QueryInterface(&This->IActiveScript_iface, riid, ppv);
550 }
551
552 static ULONG WINAPI VBScriptSafety_AddRef(IObjectSafety *iface)
553 {
554     VBScript *This = impl_from_IObjectSafety(iface);
555     return IActiveScript_AddRef(&This->IActiveScript_iface);
556 }
557
558 static ULONG WINAPI VBScriptSafety_Release(IObjectSafety *iface)
559 {
560     VBScript *This = impl_from_IObjectSafety(iface);
561     return IActiveScript_Release(&This->IActiveScript_iface);
562 }
563
564 #define SUPPORTED_OPTIONS (INTERFACESAFE_FOR_UNTRUSTED_DATA|INTERFACE_USES_DISPEX|INTERFACE_USES_SECURITY_MANAGER)
565
566 static HRESULT WINAPI VBScriptSafety_GetInterfaceSafetyOptions(IObjectSafety *iface, REFIID riid,
567         DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)
568 {
569     VBScript *This = impl_from_IObjectSafety(iface);
570
571     TRACE("(%p)->(%s %p %p)\n", This, debugstr_guid(riid), pdwSupportedOptions, pdwEnabledOptions);
572
573     if(!pdwSupportedOptions || !pdwEnabledOptions)
574         return E_POINTER;
575
576     *pdwSupportedOptions = SUPPORTED_OPTIONS;
577     *pdwEnabledOptions = This->safeopt;
578     return S_OK;
579 }
580
581 static HRESULT WINAPI VBScriptSafety_SetInterfaceSafetyOptions(IObjectSafety *iface, REFIID riid,
582         DWORD dwOptionSetMask, DWORD dwEnabledOptions)
583 {
584     VBScript *This = impl_from_IObjectSafety(iface);
585
586     TRACE("(%p)->(%s %x %x)\n", This, debugstr_guid(riid), dwOptionSetMask, dwEnabledOptions);
587
588     if(dwOptionSetMask & ~SUPPORTED_OPTIONS)
589         return E_FAIL;
590
591     This->safeopt = (dwEnabledOptions & dwOptionSetMask) | (This->safeopt & ~dwOptionSetMask) | INTERFACE_USES_DISPEX;
592     return S_OK;
593 }
594
595 static const IObjectSafetyVtbl VBScriptSafetyVtbl = {
596     VBScriptSafety_QueryInterface,
597     VBScriptSafety_AddRef,
598     VBScriptSafety_Release,
599     VBScriptSafety_GetInterfaceSafetyOptions,
600     VBScriptSafety_SetInterfaceSafetyOptions
601 };
602
603 HRESULT WINAPI VBScriptFactory_CreateInstance(IClassFactory *iface, IUnknown *pUnkOuter, REFIID riid, void **ppv)
604 {
605     VBScript *ret;
606     HRESULT hres;
607
608     TRACE("(%p %s %p)\n", pUnkOuter, debugstr_guid(riid), ppv);
609
610     ret = heap_alloc_zero(sizeof(*ret));
611     if(!ret)
612         return E_OUTOFMEMORY;
613
614     ret->IActiveScript_iface.lpVtbl = &VBScriptVtbl;
615     ret->IActiveScriptParse_iface.lpVtbl = &VBScriptParseVtbl;
616     ret->IObjectSafety_iface.lpVtbl = &VBScriptSafetyVtbl;
617
618     ret->ref = 1;
619     ret->state = SCRIPTSTATE_UNINITIALIZED;
620     ret->safeopt = INTERFACE_USES_DISPEX;
621
622     hres = IActiveScript_QueryInterface(&ret->IActiveScript_iface, riid, ppv);
623     IActiveScript_Release(&ret->IActiveScript_iface);
624     return hres;
625 }