vbscript: Added sub argument tests.
[wine] / dlls / vbscript / interp.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 #include <assert.h>
20
21 #include "vbscript.h"
22
23 #include "wine/debug.h"
24
25 WINE_DEFAULT_DEBUG_CHANNEL(vbscript);
26
27
28 typedef struct {
29     vbscode_t *code;
30     instr_t *instr;
31     script_ctx_t *script;
32     function_t *func;
33
34     VARIANT *args;
35
36     unsigned stack_size;
37     unsigned top;
38     VARIANT *stack;
39 } exec_ctx_t;
40
41 typedef HRESULT (*instr_func_t)(exec_ctx_t*);
42
43 typedef enum {
44     REF_NONE,
45     REF_DISP,
46     REF_VAR,
47     REF_FUNC
48 } ref_type_t;
49
50 typedef struct {
51     ref_type_t type;
52     union {
53         struct {
54             IDispatch *disp;
55             DISPID id;
56         } d;
57         VARIANT *v;
58         function_t *f;
59     } u;
60 } ref_t;
61
62 typedef struct {
63     VARIANT *v;
64     VARIANT store;
65     BOOL owned;
66 } variant_val_t;
67
68 static BOOL lookup_dynamic_vars(dynamic_var_t *var, const WCHAR *name, ref_t *ref)
69 {
70     while(var) {
71         if(!strcmpiW(var->name, name)) {
72             ref->type = REF_VAR;
73             ref->u.v = &var->v;
74             return TRUE;
75         }
76
77         var = var->next;
78     }
79
80     return FALSE;
81 }
82
83 static HRESULT lookup_identifier(exec_ctx_t *ctx, BSTR name, ref_t *ref)
84 {
85     named_item_t *item;
86     function_t *func;
87     unsigned i;
88     DISPID id;
89     HRESULT hres;
90
91     for(i=0; i < ctx->func->arg_cnt; i++) {
92         if(!strcmpiW(ctx->func->args[i].name, name)) {
93             ref->type = REF_VAR;
94             ref->u.v = ctx->args+i;
95             return S_OK;
96         }
97     }
98
99     if(lookup_dynamic_vars(ctx->script->global_vars, name, ref))
100         return S_OK;
101
102     for(func = ctx->script->global_funcs; func; func = func->next) {
103         if(!strcmpiW(func->name, name)) {
104             ref->type = REF_FUNC;
105             ref->u.f = func;
106             return S_OK;
107         }
108     }
109
110     LIST_FOR_EACH_ENTRY(item, &ctx->script->named_items, named_item_t, entry) {
111         if(item->flags & SCRIPTITEM_GLOBALMEMBERS) {
112             hres = disp_get_id(item->disp, name, &id);
113             if(SUCCEEDED(hres)) {
114                 ref->type = REF_DISP;
115                 ref->u.d.disp = item->disp;
116                 ref->u.d.id = id;
117                 return S_OK;
118             }
119         }
120     }
121
122     if(!ctx->func->code_ctx->option_explicit)
123         FIXME("create an attempt to set\n");
124
125     ref->type = REF_NONE;
126     return S_OK;
127 }
128
129 static inline VARIANT *stack_pop(exec_ctx_t *ctx)
130 {
131     assert(ctx->top);
132     return ctx->stack + --ctx->top;
133 }
134
135 static HRESULT stack_push(exec_ctx_t *ctx, VARIANT *v)
136 {
137     if(ctx->stack_size == ctx->top) {
138         VARIANT *new_stack;
139
140         new_stack = heap_realloc(ctx->stack, ctx->stack_size*2);
141         if(!new_stack) {
142             VariantClear(v);
143             return E_OUTOFMEMORY;
144         }
145
146         ctx->stack = new_stack;
147         ctx->stack_size *= 2;
148     }
149
150     ctx->stack[ctx->top++] = *v;
151     return S_OK;
152 }
153
154 static void stack_popn(exec_ctx_t *ctx, unsigned n)
155 {
156     while(n--)
157         VariantClear(stack_pop(ctx));
158 }
159
160 static HRESULT stack_pop_val(exec_ctx_t *ctx, variant_val_t *v)
161 {
162     VARIANT *var;
163
164     var = stack_pop(ctx);
165
166     if(V_VT(var) == (VT_BYREF|VT_VARIANT)) {
167         v->owned = FALSE;
168         var = V_VARIANTREF(var);
169     }else {
170         v->owned = TRUE;
171     }
172
173     if(V_VT(var) == VT_DISPATCH) {
174         FIXME("got dispatch - get its default value\n");
175         return E_NOTIMPL;
176     }else {
177         v->v = var;
178     }
179
180     return S_OK;
181 }
182
183 static inline void release_val(variant_val_t *v)
184 {
185     if(v->owned)
186         VariantClear(v->v);
187 }
188
189 static HRESULT stack_pop_disp(exec_ctx_t *ctx, IDispatch **ret)
190 {
191     VARIANT *v = stack_pop(ctx);
192
193     if(V_VT(v) == VT_DISPATCH) {
194         *ret = V_DISPATCH(v);
195         return S_OK;
196     }
197
198     if(V_VT(v) != (VT_VARIANT|VT_BYREF)) {
199         FIXME("not supported type: %s\n", debugstr_variant(v));
200         VariantClear(v);
201         return E_FAIL;
202     }
203
204     v = V_BYREF(v);
205     if(V_VT(v) != VT_DISPATCH) {
206         FIXME("not disp %s\n", debugstr_variant(v));
207         return E_FAIL;
208     }
209
210     if(V_DISPATCH(v))
211         IDispatch_AddRef(V_DISPATCH(v));
212     *ret = V_DISPATCH(v);
213     return S_OK;
214 }
215
216 static inline void instr_jmp(exec_ctx_t *ctx, unsigned addr)
217 {
218     ctx->instr = ctx->code->instrs + addr;
219 }
220
221 static void vbstack_to_dp(exec_ctx_t *ctx, unsigned arg_cnt, DISPPARAMS *dp)
222 {
223     dp->cArgs = arg_cnt;
224     dp->rgdispidNamedArgs = NULL;
225     dp->cNamedArgs = 0;
226
227     if(arg_cnt) {
228         VARIANT tmp;
229         unsigned i;
230
231         assert(ctx->top >= arg_cnt);
232
233         for(i=1; i*2 <= arg_cnt; i++) {
234             tmp = ctx->stack[ctx->top-i];
235             ctx->stack[ctx->top-i] = ctx->stack[ctx->top-arg_cnt+i-1];
236             ctx->stack[ctx->top-arg_cnt+i-1] = tmp;
237         }
238
239         dp->rgvarg = ctx->stack + ctx->top-arg_cnt;
240     }else {
241         dp->rgvarg = NULL;
242     }
243 }
244
245 static HRESULT do_icall(exec_ctx_t *ctx, VARIANT *res)
246 {
247     BSTR identifier = ctx->instr->arg1.bstr;
248     const unsigned arg_cnt = ctx->instr->arg2.uint;
249     ref_t ref = {0};
250     DISPPARAMS dp;
251     HRESULT hres;
252
253     hres = lookup_identifier(ctx, identifier, &ref);
254     if(FAILED(hres))
255         return hres;
256
257     vbstack_to_dp(ctx, arg_cnt, &dp);
258
259     switch(ref.type) {
260     case REF_VAR:
261         if(!res) {
262             FIXME("REF_VAR no res\n");
263             return E_NOTIMPL;
264         }
265
266         if(arg_cnt) {
267             FIXME("arguments not implemented\n");
268             return E_NOTIMPL;
269         }
270
271         V_VT(res) = VT_BYREF|VT_VARIANT;
272         V_BYREF(res) = V_VT(ref.u.v) == (VT_VARIANT|VT_BYREF) ? V_VARIANTREF(ref.u.v) : ref.u.v;
273         break;
274     case REF_DISP:
275         hres = disp_call(ctx->script, ref.u.d.disp, ref.u.d.id, &dp, res);
276         if(FAILED(hres))
277             return hres;
278         break;
279     case REF_FUNC:
280         hres = exec_script(ctx->script, ref.u.f, &dp, res);
281         if(FAILED(hres))
282             return hres;
283         break;
284     case REF_NONE:
285         FIXME("%s not found\n", debugstr_w(identifier));
286         return DISP_E_UNKNOWNNAME;
287     }
288
289     stack_popn(ctx, arg_cnt);
290     return S_OK;
291 }
292
293 static HRESULT interp_icall(exec_ctx_t *ctx)
294 {
295     VARIANT v;
296     HRESULT hres;
297
298     TRACE("\n");
299
300     hres = do_icall(ctx, &v);
301     if(FAILED(hres))
302         return hres;
303
304     return stack_push(ctx, &v);
305 }
306
307 static HRESULT interp_icallv(exec_ctx_t *ctx)
308 {
309     TRACE("\n");
310     return do_icall(ctx, NULL);
311 }
312
313 static HRESULT assign_ident(exec_ctx_t *ctx, BSTR name, VARIANT *val, BOOL own_val)
314 {
315     ref_t ref;
316     HRESULT hres;
317
318     hres = lookup_identifier(ctx, name, &ref);
319     if(FAILED(hres))
320         return hres;
321
322     switch(ref.type) {
323     case REF_VAR: {
324         VARIANT *v = ref.u.v;
325
326         if(V_VT(v) == (VT_VARIANT|VT_BYREF))
327             v = V_VARIANTREF(v);
328
329         if(own_val) {
330             VariantClear(v);
331             *v = *val;
332             hres = S_OK;
333         }else {
334             hres = VariantCopy(v, val);
335         }
336         break;
337     }
338     case REF_DISP:
339         hres = disp_propput(ctx->script, ref.u.d.disp, ref.u.d.id, val);
340         if(own_val)
341             VariantClear(val);
342         break;
343     case REF_FUNC:
344         FIXME("functions not implemented\n");
345         return E_NOTIMPL;
346     case REF_NONE:
347         FIXME("%s not found\n", debugstr_w(name));
348         if(own_val)
349             VariantClear(val);
350         return DISP_E_UNKNOWNNAME;
351     }
352
353     return hres;
354 }
355
356 static HRESULT interp_assign_ident(exec_ctx_t *ctx)
357 {
358     const BSTR arg = ctx->instr->arg1.bstr;
359     variant_val_t v;
360     HRESULT hres;
361
362     TRACE("%s\n", debugstr_w(arg));
363
364     hres = stack_pop_val(ctx, &v);
365     if(FAILED(hres))
366         return hres;
367
368     return assign_ident(ctx, arg, v.v, v.owned);
369 }
370
371 static HRESULT interp_assign_member(exec_ctx_t *ctx)
372 {
373     BSTR identifier = ctx->instr->arg1.bstr;
374     variant_val_t val;
375     IDispatch *obj;
376     DISPID id;
377     HRESULT hres;
378
379     TRACE("%s\n", debugstr_w(identifier));
380
381     hres = stack_pop_disp(ctx, &obj);
382     if(FAILED(hres))
383         return hres;
384
385     if(!obj) {
386         FIXME("NULL obj\n");
387         return E_FAIL;
388     }
389
390     hres = stack_pop_val(ctx, &val);
391     if(FAILED(hres)) {
392         IDispatch_Release(obj);
393         return hres;
394     }
395
396     hres = disp_get_id(obj, identifier, &id);
397     if(SUCCEEDED(hres))
398         hres = disp_propput(ctx->script, obj, id, val.v);
399
400     release_val(&val);
401     IDispatch_Release(obj);
402     return hres;
403 }
404
405 static HRESULT interp_jmp(exec_ctx_t *ctx)
406 {
407     const unsigned arg = ctx->instr->arg1.uint;
408
409     TRACE("%u\n", arg);
410
411     instr_jmp(ctx, arg);
412     return S_OK;
413 }
414
415 static HRESULT interp_jmp_false(exec_ctx_t *ctx)
416 {
417     const unsigned arg = ctx->instr->arg1.uint;
418     variant_val_t val;
419     HRESULT hres;
420
421     TRACE("%u\n", arg);
422
423     hres = stack_pop_val(ctx, &val);
424     if(FAILED(hres))
425         return hres;
426
427     if(V_VT(val.v) != VT_BOOL) {
428         FIXME("unsupported for %s\n", debugstr_variant(val.v));
429         release_val(&val);
430         return E_NOTIMPL;
431     }
432
433     if(V_BOOL(val.v))
434         ctx->instr++;
435     else
436         instr_jmp(ctx, ctx->instr->arg1.uint);
437     return S_OK;
438 }
439
440 static HRESULT interp_ret(exec_ctx_t *ctx)
441 {
442     TRACE("\n");
443
444     ctx->instr = NULL;
445     return S_OK;
446 }
447
448 static HRESULT interp_bool(exec_ctx_t *ctx)
449 {
450     const VARIANT_BOOL arg = ctx->instr->arg1.lng;
451     VARIANT v;
452
453     TRACE("%s\n", arg ? "true" : "false");
454
455     V_VT(&v) = VT_BOOL;
456     V_BOOL(&v) = arg;
457     return stack_push(ctx, &v);
458 }
459
460 static HRESULT interp_string(exec_ctx_t *ctx)
461 {
462     VARIANT v;
463
464     TRACE("\n");
465
466     V_VT(&v) = VT_BSTR;
467     V_BSTR(&v) = SysAllocString(ctx->instr->arg1.str);
468     if(!V_BSTR(&v))
469         return E_OUTOFMEMORY;
470
471     return stack_push(ctx, &v);
472 }
473
474 static HRESULT interp_long(exec_ctx_t *ctx)
475 {
476     const LONG arg = ctx->instr->arg1.lng;
477     VARIANT v;
478
479     TRACE("%d\n", arg);
480
481     V_VT(&v) = VT_I4;
482     V_I4(&v) = arg;
483     return stack_push(ctx, &v);
484 }
485
486 static HRESULT interp_short(exec_ctx_t *ctx)
487 {
488     const LONG arg = ctx->instr->arg1.lng;
489     VARIANT v;
490
491     TRACE("%d\n", arg);
492
493     V_VT(&v) = VT_I2;
494     V_I2(&v) = arg;
495     return stack_push(ctx, &v);
496 }
497
498 static HRESULT interp_double(exec_ctx_t *ctx)
499 {
500     const DOUBLE *arg = ctx->instr->arg1.dbl;
501     VARIANT v;
502
503     TRACE("%lf\n", *arg);
504
505     V_VT(&v) = VT_R8;
506     V_R8(&v) = *arg;
507     return stack_push(ctx, &v);
508 }
509
510 static HRESULT interp_empty(exec_ctx_t *ctx)
511 {
512     VARIANT v;
513
514     TRACE("\n");
515
516     V_VT(&v) = VT_EMPTY;
517     return stack_push(ctx, &v);
518 }
519
520 static HRESULT interp_null(exec_ctx_t *ctx)
521 {
522     VARIANT v;
523
524     TRACE("\n");
525
526     V_VT(&v) = VT_NULL;
527     return stack_push(ctx, &v);
528 }
529
530 static HRESULT interp_not(exec_ctx_t *ctx)
531 {
532     variant_val_t val;
533     VARIANT v;
534     HRESULT hres;
535
536     TRACE("\n");
537
538     hres = stack_pop_val(ctx, &val);
539     if(FAILED(hres))
540         return hres;
541
542     hres = VarNot(val.v, &v);
543     release_val(&val);
544     if(FAILED(hres))
545         return hres;
546
547     return stack_push(ctx, &v);
548 }
549
550 static HRESULT cmp_oper(exec_ctx_t *ctx)
551 {
552     variant_val_t l, r;
553     HRESULT hres;
554
555     hres = stack_pop_val(ctx, &r);
556     if(FAILED(hres))
557         return hres;
558
559     hres = stack_pop_val(ctx, &l);
560     if(SUCCEEDED(hres)) {
561         if(V_VT(l.v) == VT_NULL || V_VT(r.v) == VT_NULL) {
562             FIXME("comparing nulls is not implemented\n");
563             hres = E_NOTIMPL;
564         }else {
565             hres = VarCmp(l.v, r.v, ctx->script->lcid, 0);
566         }
567     }
568
569     release_val(&r);
570     release_val(&l);
571     return hres;
572 }
573
574 static HRESULT interp_equal(exec_ctx_t *ctx)
575 {
576     VARIANT v;
577     HRESULT hres;
578
579     TRACE("\n");
580
581     hres = cmp_oper(ctx);
582     if(FAILED(hres))
583         return hres;
584
585     V_VT(&v) = VT_BOOL;
586     V_BOOL(&v) = hres == VARCMP_EQ ? VARIANT_TRUE : VARIANT_FALSE;
587     return stack_push(ctx, &v);
588 }
589
590 static HRESULT interp_nequal(exec_ctx_t *ctx)
591 {
592     VARIANT v;
593     HRESULT hres;
594
595     TRACE("\n");
596
597     hres = cmp_oper(ctx);
598     if(FAILED(hres))
599         return hres;
600
601     V_VT(&v) = VT_BOOL;
602     V_BOOL(&v) = hres != VARCMP_EQ ? VARIANT_TRUE : VARIANT_FALSE;
603     return stack_push(ctx, &v);
604 }
605
606 static HRESULT interp_concat(exec_ctx_t *ctx)
607 {
608     variant_val_t r, l;
609     VARIANT v;
610     HRESULT hres;
611
612     TRACE("\n");
613
614     hres = stack_pop_val(ctx, &r);
615     if(FAILED(hres))
616         return hres;
617
618     hres = stack_pop_val(ctx, &l);
619     if(SUCCEEDED(hres)) {
620         hres = VarCat(l.v, r.v, &v);
621         release_val(&l);
622     }
623     release_val(&r);
624     if(FAILED(hres))
625         return hres;
626
627     return stack_push(ctx, &v);
628 }
629
630 static HRESULT interp_add(exec_ctx_t *ctx)
631 {
632     variant_val_t r, l;
633     VARIANT v;
634     HRESULT hres;
635
636     TRACE("\n");
637
638     hres = stack_pop_val(ctx, &r);
639     if(FAILED(hres))
640         return hres;
641
642     hres = stack_pop_val(ctx, &l);
643     if(SUCCEEDED(hres)) {
644         hres = VarAdd(l.v, r.v, &v);
645         release_val(&l);
646     }
647     release_val(&r);
648     if(FAILED(hres))
649         return hres;
650
651     return stack_push(ctx, &v);
652 }
653
654 static HRESULT interp_sub(exec_ctx_t *ctx)
655 {
656     variant_val_t r, l;
657     VARIANT v;
658     HRESULT hres;
659
660     TRACE("\n");
661
662     hres = stack_pop_val(ctx, &r);
663     if(FAILED(hres))
664         return hres;
665
666     hres = stack_pop_val(ctx, &l);
667     if(SUCCEEDED(hres)) {
668         hres = VarSub(l.v, r.v, &v);
669         release_val(&l);
670     }
671     release_val(&r);
672     if(FAILED(hres))
673         return hres;
674
675     return stack_push(ctx, &v);
676 }
677
678 static HRESULT interp_mod(exec_ctx_t *ctx)
679 {
680     variant_val_t r, l;
681     VARIANT v;
682     HRESULT hres;
683
684     TRACE("\n");
685
686     hres = stack_pop_val(ctx, &r);
687     if(FAILED(hres))
688         return hres;
689
690     hres = stack_pop_val(ctx, &l);
691     if(SUCCEEDED(hres)) {
692         hres = VarMod(l.v, r.v, &v);
693         release_val(&l);
694     }
695     release_val(&r);
696     if(FAILED(hres))
697         return hres;
698
699     return stack_push(ctx, &v);
700 }
701
702 static HRESULT interp_idiv(exec_ctx_t *ctx)
703 {
704     variant_val_t r, l;
705     VARIANT v;
706     HRESULT hres;
707
708     TRACE("\n");
709
710     hres = stack_pop_val(ctx, &r);
711     if(FAILED(hres))
712         return hres;
713
714     hres = stack_pop_val(ctx, &l);
715     if(SUCCEEDED(hres)) {
716         hres = VarIdiv(l.v, r.v, &v);
717         release_val(&l);
718     }
719     release_val(&r);
720     if(FAILED(hres))
721         return hres;
722
723     return stack_push(ctx, &v);
724 }
725
726 static HRESULT interp_div(exec_ctx_t *ctx)
727 {
728     variant_val_t r, l;
729     VARIANT v;
730     HRESULT hres;
731
732     TRACE("\n");
733
734     hres = stack_pop_val(ctx, &r);
735     if(FAILED(hres))
736         return hres;
737
738     hres = stack_pop_val(ctx, &l);
739     if(SUCCEEDED(hres)) {
740         hres = VarDiv(l.v, r.v, &v);
741         release_val(&l);
742     }
743     release_val(&r);
744     if(FAILED(hres))
745         return hres;
746
747     return stack_push(ctx, &v);
748 }
749
750 static HRESULT interp_mul(exec_ctx_t *ctx)
751 {
752     variant_val_t r, l;
753     VARIANT v;
754     HRESULT hres;
755
756     TRACE("\n");
757
758     hres = stack_pop_val(ctx, &r);
759     if(FAILED(hres))
760         return hres;
761
762     hres = stack_pop_val(ctx, &l);
763     if(SUCCEEDED(hres)) {
764         hres = VarMul(l.v, r.v, &v);
765         release_val(&l);
766     }
767     release_val(&r);
768     if(FAILED(hres))
769         return hres;
770
771     return stack_push(ctx, &v);
772 }
773
774 static HRESULT interp_exp(exec_ctx_t *ctx)
775 {
776     variant_val_t r, l;
777     VARIANT v;
778     HRESULT hres;
779
780     TRACE("\n");
781
782     hres = stack_pop_val(ctx, &r);
783     if(FAILED(hres))
784         return hres;
785
786     hres = stack_pop_val(ctx, &l);
787     if(SUCCEEDED(hres)) {
788         hres = VarPow(l.v, r.v, &v);
789         release_val(&l);
790     }
791     release_val(&r);
792     if(FAILED(hres))
793         return hres;
794
795     return stack_push(ctx, &v);
796 }
797
798 static HRESULT interp_neg(exec_ctx_t *ctx)
799 {
800     variant_val_t val;
801     VARIANT v;
802     HRESULT hres;
803
804     hres = stack_pop_val(ctx, &val);
805     if(FAILED(hres))
806         return hres;
807
808     hres = VarNeg(val.v, &v);
809     release_val(&val);
810     if(FAILED(hres))
811         return hres;
812
813     return stack_push(ctx, &v);
814 }
815
816 static const instr_func_t op_funcs[] = {
817 #define X(x,n,a,b) interp_ ## x,
818 OP_LIST
819 #undef X
820 };
821
822 static const unsigned op_move[] = {
823 #define X(x,n,a,b) n,
824 OP_LIST
825 #undef X
826 };
827
828 static void release_exec(exec_ctx_t *ctx)
829 {
830     if(ctx->args) {
831         unsigned i;
832
833         for(i=0; i < ctx->func->arg_cnt; i++)
834             VariantClear(ctx->args+i);
835     }
836
837     heap_free(ctx->args);
838     heap_free(ctx->stack);
839 }
840
841 HRESULT exec_script(script_ctx_t *ctx, function_t *func, DISPPARAMS *dp, VARIANT *res)
842 {
843     exec_ctx_t exec = {func->code_ctx};
844     vbsop_t op;
845     HRESULT hres = S_OK;
846
847     if(res) {
848         FIXME("returning value is not implemented\n");
849         return E_NOTIMPL;
850     }
851
852     exec.code = func->code_ctx;
853
854     if(dp ? func->arg_cnt != arg_cnt(dp) : func->arg_cnt) {
855         FIXME("wrong arg_cnt %d, expected %d\n", dp ? arg_cnt(dp) : 0, func->arg_cnt);
856         return E_FAIL;
857     }
858
859     if(func->arg_cnt) {
860         VARIANT *v;
861         unsigned i;
862
863         exec.args = heap_alloc_zero(func->arg_cnt * sizeof(VARIANT));
864         if(!exec.args) {
865             release_exec(&exec);
866             return E_OUTOFMEMORY;
867         }
868
869         for(i=0; i < func->arg_cnt; i++) {
870             v = get_arg(dp, i);
871             if(V_VT(v) == (VT_VARIANT|VT_BYREF)) {
872                 if(func->args[i].by_ref)
873                     exec.args[i] = *v;
874                 else
875                     hres = VariantCopy(exec.args+i, V_VARIANTREF(v));
876             }else {
877                 hres = VariantCopy(exec.args+i, v);
878             }
879             if(FAILED(hres)) {
880                 release_exec(&exec);
881                 return hres;
882             }
883         }
884     }else {
885         exec.args = NULL;
886     }
887
888     exec.stack_size = 16;
889     exec.top = 0;
890     exec.stack = heap_alloc(exec.stack_size * sizeof(VARIANT));
891     if(!exec.stack) {
892         release_exec(&exec);
893         return E_OUTOFMEMORY;
894     }
895
896     exec.instr = exec.code->instrs + func->code_off;
897     exec.script = ctx;
898     exec.func = func;
899
900     while(exec.instr) {
901         op = exec.instr->op;
902         hres = op_funcs[op](&exec);
903         if(FAILED(hres)) {
904             FIXME("Failed %08x\n", hres);
905             stack_popn(&exec, exec.top);
906             break;
907         }
908
909         exec.instr += op_move[op];
910     }
911
912     assert(!exec.top);
913     release_exec(&exec);
914
915     return hres;
916 }