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