jscript: Use jsval instead of VARIANT to pass arguments to builtin functions.
[wine] / dlls / jscript / function.c
1 /*
2  * Copyright 2008 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 "jscript.h"
22 #include "engine.h"
23
24 #include "wine/debug.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
27
28 typedef struct {
29     jsdisp_t dispex;
30     builtin_invoke_t value_proc;
31     const WCHAR *name;
32     DWORD flags;
33     scope_chain_t *scope_chain;
34     bytecode_t *code;
35     function_code_t *func_code;
36     DWORD length;
37     jsdisp_t *arguments;
38 } FunctionInstance;
39
40 static inline FunctionInstance *function_from_vdisp(vdisp_t *vdisp)
41 {
42     return (FunctionInstance*)vdisp->u.jsdisp;
43 }
44
45 static inline FunctionInstance *function_this(vdisp_t *jsthis)
46 {
47     return is_vclass(jsthis, JSCLASS_FUNCTION) ? function_from_vdisp(jsthis) : NULL;
48 }
49
50 static const WCHAR prototypeW[] = {'p','r','o','t','o','t', 'y', 'p','e',0};
51
52 static const WCHAR lengthW[] = {'l','e','n','g','t','h',0};
53 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
54 static const WCHAR applyW[] = {'a','p','p','l','y',0};
55 static const WCHAR callW[] = {'c','a','l','l',0};
56 static const WCHAR argumentsW[] = {'a','r','g','u','m','e','n','t','s',0};
57
58 static HRESULT init_parameters(jsdisp_t *var_disp, FunctionInstance *function, unsigned argc, jsval_t *argv,
59         jsexcept_t *ei)
60 {
61     DWORD i=0;
62     HRESULT hres;
63
64     for(i=0; i < function->func_code->param_cnt; i++) {
65         hres = jsdisp_propput_name(var_disp, function->func_code->params[i],
66                 i < argc ? argv[i] : jsval_undefined(), ei);
67         if(FAILED(hres))
68             return hres;
69     }
70
71     return S_OK;
72 }
73
74 static HRESULT Arguments_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
75         jsval_t *r, jsexcept_t *ei)
76 {
77     FIXME("\n");
78     return E_NOTIMPL;
79 }
80
81 static const builtin_info_t Arguments_info = {
82     JSCLASS_ARGUMENTS,
83     {NULL, Arguments_value, 0},
84     0, NULL,
85     NULL,
86     NULL
87 };
88
89 static HRESULT create_arguments(script_ctx_t *ctx, IDispatch *calee, unsigned argc, jsval_t *argv,
90         jsexcept_t *ei, jsdisp_t **ret)
91 {
92     jsdisp_t *args;
93     DWORD i;
94     HRESULT hres;
95
96     static const WCHAR caleeW[] = {'c','a','l','l','e','e',0};
97
98     args = heap_alloc_zero(sizeof(jsdisp_t));
99     if(!args)
100         return E_OUTOFMEMORY;
101
102     hres = init_dispex_from_constr(args, ctx, &Arguments_info, ctx->object_constr);
103     if(FAILED(hres)) {
104         heap_free(args);
105         return hres;
106     }
107
108     for(i=0; i < argc; i++) {
109         hres = jsdisp_propput_idx(args, i, argv[i], ei);
110         if(FAILED(hres))
111             break;
112     }
113
114     if(SUCCEEDED(hres)) {
115         hres = jsdisp_propput_name(args, lengthW, jsval_number(argc), ei);
116
117         if(SUCCEEDED(hres))
118             hres = jsdisp_propput_name(args, caleeW, jsval_disp(calee), ei);
119     }
120
121     if(FAILED(hres)) {
122         jsdisp_release(args);
123         return hres;
124     }
125
126     *ret = args;
127     return S_OK;
128 }
129
130 static HRESULT create_var_disp(script_ctx_t *ctx, FunctionInstance *function, jsdisp_t *arg_disp,
131         unsigned argc, jsval_t *argv, jsexcept_t *ei, jsdisp_t **ret)
132 {
133     jsdisp_t *var_disp;
134     HRESULT hres;
135
136     hres = create_dispex(ctx, NULL, NULL, &var_disp);
137     if(FAILED(hres))
138         return hres;
139
140     hres = jsdisp_propput_name(var_disp, argumentsW, jsval_obj(arg_disp), ei);
141     if(SUCCEEDED(hres))
142         hres = init_parameters(var_disp, function, argc, argv, ei);
143     if(FAILED(hres)) {
144         jsdisp_release(var_disp);
145         return hres;
146     }
147
148     *ret = var_disp;
149     return S_OK;
150 }
151
152 static HRESULT invoke_source(script_ctx_t *ctx, FunctionInstance *function, IDispatch *this_obj, unsigned argc, jsval_t *argv,
153         jsval_t *r, jsexcept_t *ei)
154 {
155     jsdisp_t *var_disp, *arg_disp;
156     exec_ctx_t *exec_ctx;
157     scope_chain_t *scope;
158     HRESULT hres;
159
160     if(!function->func_code) {
161         FIXME("no source\n");
162         return E_FAIL;
163     }
164
165     hres = create_arguments(ctx, to_disp(&function->dispex), argc, argv, ei, &arg_disp);
166     if(FAILED(hres))
167         return hres;
168
169     hres = create_var_disp(ctx, function, arg_disp, argc, argv, ei, &var_disp);
170     if(FAILED(hres)) {
171         jsdisp_release(arg_disp);
172         return hres;
173     }
174
175     hres = scope_push(function->scope_chain, var_disp, to_disp(var_disp), &scope);
176     if(SUCCEEDED(hres)) {
177         hres = create_exec_ctx(ctx, this_obj, var_disp, scope, FALSE, &exec_ctx);
178         scope_release(scope);
179     }
180     jsdisp_release(var_disp);
181     if(SUCCEEDED(hres)) {
182         jsdisp_t *prev_args;
183         VARIANT retv;
184
185         prev_args = function->arguments;
186         function->arguments = arg_disp;
187         hres = exec_source(exec_ctx, function->code, function->func_code, FALSE, ei, r ? &retv : NULL);
188         function->arguments = prev_args;
189         if(SUCCEEDED(hres) && r) {
190             hres = variant_to_jsval(&retv, r);
191             VariantClear(&retv);
192         }
193     }
194
195     jsdisp_release(arg_disp);
196     exec_release(exec_ctx);
197     return hres;
198 }
199
200 static HRESULT invoke_constructor(script_ctx_t *ctx, FunctionInstance *function, unsigned argc, jsval_t *argv,
201         jsval_t *r, jsexcept_t *ei)
202 {
203     jsdisp_t *this_obj;
204     jsval_t var;
205     HRESULT hres;
206
207     hres = create_object(ctx, &function->dispex, &this_obj);
208     if(FAILED(hres))
209         return hres;
210
211     hres = invoke_source(ctx, function, to_disp(this_obj), argc, argv, &var, ei);
212     if(FAILED(hres)) {
213         jsdisp_release(this_obj);
214         return hres;
215     }
216
217     if(is_object_instance(var)) {
218         jsdisp_release(this_obj);
219         *r = var;
220     }else {
221         jsval_release(var);
222         *r = jsval_obj(this_obj);
223     }
224     return S_OK;
225 }
226
227 static HRESULT invoke_value_proc(script_ctx_t *ctx, FunctionInstance *function, IDispatch *this_disp, WORD flags, unsigned argc, jsval_t *argv,
228         jsval_t *r, jsexcept_t *ei)
229 {
230     vdisp_t vthis;
231     HRESULT hres;
232
233     if(this_disp)
234         set_disp(&vthis, this_disp);
235     else if(ctx->host_global)
236         set_disp(&vthis, ctx->host_global);
237     else
238         set_jsdisp(&vthis, ctx->global);
239
240     hres = function->value_proc(ctx, &vthis, flags, argc, argv, r, ei);
241
242     vdisp_release(&vthis);
243     return hres;
244 }
245
246 static HRESULT call_function(script_ctx_t *ctx, FunctionInstance *function, IDispatch *this_obj,
247         unsigned argc, jsval_t *argv, jsval_t *r, jsexcept_t *ei)
248 {
249     if(function->value_proc)
250         return invoke_value_proc(ctx, function, this_obj, DISPATCH_METHOD, argc, argv, r, ei);
251
252     return invoke_source(ctx, function, this_obj, argc, argv, r, ei);
253 }
254
255 static HRESULT function_to_string(FunctionInstance *function, BSTR *ret)
256 {
257     BSTR str;
258
259     static const WCHAR native_prefixW[] = {'\n','f','u','n','c','t','i','o','n',' '};
260     static const WCHAR native_suffixW[] =
261         {'(',')',' ','{','\n',' ',' ',' ',' ','[','n','a','t','i','v','e',' ','c','o','d','e',']','\n','}','\n'};
262
263     if(function->value_proc) {
264         DWORD name_len;
265
266         name_len = strlenW(function->name);
267         str = SysAllocStringLen(NULL, sizeof(native_prefixW) + name_len*sizeof(WCHAR) + sizeof(native_suffixW));
268         if(!str)
269             return E_OUTOFMEMORY;
270
271         memcpy(str, native_prefixW, sizeof(native_prefixW));
272         memcpy(str + sizeof(native_prefixW)/sizeof(WCHAR), function->name, name_len*sizeof(WCHAR));
273         memcpy(str + sizeof(native_prefixW)/sizeof(WCHAR) + name_len, native_suffixW, sizeof(native_suffixW));
274     }else {
275         str = SysAllocStringLen(function->func_code->source, function->func_code->source_len);
276         if(!str)
277             return E_OUTOFMEMORY;
278     }
279
280     *ret = str;
281     return S_OK;
282 }
283
284 HRESULT Function_invoke(jsdisp_t *func_this, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r, jsexcept_t *ei)
285 {
286     FunctionInstance *function;
287
288     TRACE("func %p this %p\n", func_this, jsthis);
289
290     assert(is_class(func_this, JSCLASS_FUNCTION));
291     function = (FunctionInstance*)func_this;
292
293     if(function->value_proc)
294         return invoke_value_proc(function->dispex.ctx, function, jsthis, flags, argc, argv, r, ei);
295
296     if(flags == DISPATCH_CONSTRUCT)
297         return invoke_constructor(function->dispex.ctx, function, argc, argv, r, ei);
298
299     assert(flags == DISPATCH_METHOD);
300     return invoke_source(function->dispex.ctx, function, jsthis, argc, argv, r, ei);
301 }
302
303 static HRESULT Function_length(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
304         jsval_t *r, jsexcept_t *ei)
305 {
306     FunctionInstance *This = function_from_vdisp(jsthis);
307
308     TRACE("%p %d\n", This, This->length);
309
310     switch(flags) {
311     case DISPATCH_PROPERTYGET:
312         *r = jsval_number(This->length);
313         break;
314     default:
315         FIXME("unimplemented flags %x\n", flags);
316         return E_NOTIMPL;
317     }
318
319     return S_OK;
320 }
321
322 static HRESULT Function_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
323         jsval_t *r, jsexcept_t *ei)
324 {
325     FunctionInstance *function;
326     BSTR str;
327     HRESULT hres;
328
329     TRACE("\n");
330
331     if(!(function = function_this(jsthis)))
332         return throw_type_error(ctx, ei, JS_E_FUNCTION_EXPECTED, NULL);
333
334     hres = function_to_string(function, &str);
335     if(FAILED(hres))
336         return hres;
337
338     if(r)
339         *r = jsval_string(str);
340     else
341         SysFreeString(str);
342     return S_OK;
343 }
344
345 static HRESULT array_to_args(script_ctx_t *ctx, jsdisp_t *arg_array, jsexcept_t *ei, unsigned *argc, jsval_t **ret)
346 {
347     jsval_t *argv, val;
348     DWORD length, i;
349     HRESULT hres;
350
351     hres = jsdisp_propget_name(arg_array, lengthW, &val, ei);
352     if(FAILED(hres))
353         return hres;
354
355     hres = to_uint32_jsval(ctx, val, ei, &length);
356     jsval_release(val);
357     if(FAILED(hres))
358         return hres;
359
360     argv = heap_alloc(length * sizeof(*argv));
361     if(!argv)
362         return E_OUTOFMEMORY;
363
364     for(i=0; i<length; i++) {
365         hres = jsdisp_get_idx(arg_array, i, argv+i, ei);
366         if(hres == DISP_E_UNKNOWNNAME) {
367             argv[i] = jsval_undefined();
368         }else if(FAILED(hres)) {
369             while(i--)
370                 jsval_release(argv[i]);
371             heap_free(argv);
372             return hres;
373         }
374     }
375
376     *argc = length;
377     *ret = argv;
378     return S_OK;
379 }
380
381 static HRESULT Function_apply(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
382         jsval_t *r, jsexcept_t *ei)
383 {
384     FunctionInstance *function;
385     jsval_t *args = NULL;
386     unsigned i, cnt = 0;
387     IDispatch *this_obj = NULL;
388     HRESULT hres = S_OK;
389
390     TRACE("\n");
391
392     if(!(function = function_this(jsthis)))
393         return throw_type_error(ctx, ei, JS_E_FUNCTION_EXPECTED, NULL);
394
395     if(argc) {
396         if(!is_undefined(argv[0]) && !is_null(argv[0])) {
397             hres = to_object_jsval(ctx, argv[0], &this_obj);
398             if(FAILED(hres))
399                 return hres;
400         }
401     }
402
403     if(argc >= 2) {
404         jsdisp_t *arg_array = NULL;
405
406         if(is_object_instance(argv[1])) {
407             arg_array = iface_to_jsdisp((IUnknown*)get_object(argv[1]));
408             if(arg_array &&
409                (!is_class(arg_array, JSCLASS_ARRAY) && !is_class(arg_array, JSCLASS_ARGUMENTS) )) {
410                 jsdisp_release(arg_array);
411                 arg_array = NULL;
412             }
413         }
414
415         if(arg_array) {
416             hres = array_to_args(ctx, arg_array, ei, &cnt, &args);
417             jsdisp_release(arg_array);
418         }else {
419             FIXME("throw TypeError\n");
420             hres = E_FAIL;
421         }
422     }
423
424     if(SUCCEEDED(hres))
425         hres = call_function(ctx, function, this_obj, cnt, args, r, ei);
426
427     if(this_obj)
428         IDispatch_Release(this_obj);
429     for(i=0; i < cnt; i++)
430         jsval_release(args[i]);
431     heap_free(args);
432     return hres;
433 }
434
435 static HRESULT Function_call(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
436         jsval_t *r, jsexcept_t *ei)
437 {
438     FunctionInstance *function;
439     IDispatch *this_obj = NULL;
440     unsigned cnt = 0;
441     HRESULT hres;
442
443     TRACE("\n");
444
445     if(!(function = function_this(jsthis)))
446         return throw_type_error(ctx, ei, JS_E_FUNCTION_EXPECTED, NULL);
447
448     if(argc) {
449         if(!is_undefined(argv[0]) && !is_null(argv[0])) {
450             hres = to_object_jsval(ctx, argv[0], &this_obj);
451             if(FAILED(hres))
452                 return hres;
453         }
454
455         cnt = argc-1;
456     }
457
458     hres = call_function(ctx, function, this_obj, cnt, argv+1, r, ei);
459
460     if(this_obj)
461         IDispatch_Release(this_obj);
462     return hres;
463 }
464
465 HRESULT Function_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
466         jsval_t *r, jsexcept_t *ei)
467 {
468     FunctionInstance *function;
469
470     TRACE("\n");
471
472     if(!is_vclass(jsthis, JSCLASS_FUNCTION)) {
473         ERR("dispex is not a function\n");
474         return E_FAIL;
475     }
476
477     function = (FunctionInstance*)jsthis->u.jsdisp;
478
479     switch(flags) {
480     case DISPATCH_METHOD:
481         assert(function->value_proc != NULL);
482         return invoke_value_proc(ctx, function, NULL, flags, argc, argv, r, ei);
483
484     case DISPATCH_PROPERTYGET: {
485         HRESULT hres;
486         BSTR str;
487
488         hres = function_to_string(function, &str);
489         if(FAILED(hres))
490             return hres;
491
492         *r = jsval_string(str);
493         break;
494     }
495
496     case DISPATCH_CONSTRUCT:
497         assert(function->value_proc != NULL);
498         return invoke_value_proc(ctx, function, NULL, flags, argc, argv, r, ei);
499
500     default:
501         FIXME("not implemented flags %x\n", flags);
502         return E_NOTIMPL;
503     }
504
505     return S_OK;
506 }
507
508 static HRESULT Function_arguments(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
509         unsigned argc, jsval_t *argv, jsval_t *r, jsexcept_t *ei)
510 {
511     FunctionInstance *function = (FunctionInstance*)jsthis->u.jsdisp;
512     HRESULT hres = S_OK;
513
514     TRACE("\n");
515
516     switch(flags) {
517     case DISPATCH_PROPERTYGET: {
518         *r = function->arguments ? jsval_obj(jsdisp_addref(function->arguments)) : jsval_null();
519         break;
520     }
521     case DISPATCH_PROPERTYPUT:
522         break;
523     default:
524         FIXME("unimplemented flags %x\n", flags);
525         hres = E_NOTIMPL;
526     }
527
528     return hres;
529 }
530
531 static void Function_destructor(jsdisp_t *dispex)
532 {
533     FunctionInstance *This = (FunctionInstance*)dispex;
534
535     if(This->code)
536         release_bytecode(This->code);
537     if(This->scope_chain)
538         scope_release(This->scope_chain);
539     heap_free(This);
540 }
541
542 static const builtin_prop_t Function_props[] = {
543     {applyW,                 Function_apply,                 PROPF_METHOD|2},
544     {argumentsW,             Function_arguments,             0},
545     {callW,                  Function_call,                  PROPF_METHOD|1},
546     {lengthW,                Function_length,                0},
547     {toStringW,              Function_toString,              PROPF_METHOD}
548 };
549
550 static const builtin_info_t Function_info = {
551     JSCLASS_FUNCTION,
552     {NULL, Function_value, 0},
553     sizeof(Function_props)/sizeof(*Function_props),
554     Function_props,
555     Function_destructor,
556     NULL
557 };
558
559 static const builtin_prop_t FunctionInst_props[] = {
560     {argumentsW,             Function_arguments,             0},
561     {lengthW,                Function_length,                0}
562 };
563
564 static const builtin_info_t FunctionInst_info = {
565     JSCLASS_FUNCTION,
566     {NULL, Function_value, 0},
567     sizeof(FunctionInst_props)/sizeof(*FunctionInst_props),
568     FunctionInst_props,
569     Function_destructor,
570     NULL
571 };
572
573 static HRESULT create_function(script_ctx_t *ctx, const builtin_info_t *builtin_info, DWORD flags,
574         BOOL funcprot, jsdisp_t *prototype, FunctionInstance **ret)
575 {
576     FunctionInstance *function;
577     HRESULT hres;
578
579     function = heap_alloc_zero(sizeof(FunctionInstance));
580     if(!function)
581         return E_OUTOFMEMORY;
582
583     if(funcprot)
584         hres = init_dispex(&function->dispex, ctx, builtin_info, prototype);
585     else if(builtin_info)
586         hres = init_dispex_from_constr(&function->dispex, ctx, builtin_info, ctx->function_constr);
587     else
588         hres = init_dispex_from_constr(&function->dispex, ctx, &FunctionInst_info, ctx->function_constr);
589     if(FAILED(hres))
590         return hres;
591
592     function->flags = flags;
593     function->length = flags & PROPF_ARGMASK;
594
595     *ret = function;
596     return S_OK;
597 }
598
599 static HRESULT set_prototype(script_ctx_t *ctx, jsdisp_t *dispex, jsdisp_t *prototype)
600 {
601     jsexcept_t jsexcept;
602
603     memset(&jsexcept, 0, sizeof(jsexcept));
604     return jsdisp_propput_name(dispex, prototypeW, jsval_obj(prototype), &jsexcept);
605 }
606
607 HRESULT create_builtin_function(script_ctx_t *ctx, builtin_invoke_t value_proc, const WCHAR *name,
608         const builtin_info_t *builtin_info, DWORD flags, jsdisp_t *prototype, jsdisp_t **ret)
609 {
610     FunctionInstance *function;
611     HRESULT hres;
612
613     hres = create_function(ctx, builtin_info, flags, FALSE, NULL, &function);
614     if(FAILED(hres))
615         return hres;
616
617     if(builtin_info) {
618         VARIANT var;
619
620         num_set_int(&var, function->length);
621         hres = jsdisp_propput_const(&function->dispex, lengthW, &var);
622     }
623
624     if(SUCCEEDED(hres))
625         hres = set_prototype(ctx, &function->dispex, prototype);
626     if(FAILED(hres)) {
627         jsdisp_release(&function->dispex);
628         return hres;
629     }
630
631     function->value_proc = value_proc;
632     function->name = name;
633
634     *ret = &function->dispex;
635     return S_OK;
636 }
637
638 static HRESULT set_constructor_prop(script_ctx_t *ctx, jsdisp_t *constr, jsdisp_t *prot)
639 {
640     VARIANT v;
641
642     static const WCHAR constructorW[] = {'c','o','n','s','t','r','u','c','t','o','r',0};
643
644     V_VT(&v) = VT_DISPATCH;
645     V_DISPATCH(&v) = to_disp(constr);
646     return jsdisp_propput_dontenum(prot, constructorW, &v);
647 }
648
649 HRESULT create_builtin_constructor(script_ctx_t *ctx, builtin_invoke_t value_proc, const WCHAR *name,
650         const builtin_info_t *builtin_info, DWORD flags, jsdisp_t *prototype, jsdisp_t **ret)
651 {
652     jsdisp_t *constr;
653     HRESULT hres;
654
655     hres = create_builtin_function(ctx, value_proc, name, builtin_info, flags, prototype, &constr);
656     if(FAILED(hres))
657         return hres;
658
659     hres = set_constructor_prop(ctx, constr, prototype);
660     if(FAILED(hres)) {
661         jsdisp_release(constr);
662         return hres;
663     }
664
665     *ret = constr;
666     return S_OK;
667 }
668
669 HRESULT create_source_function(script_ctx_t *ctx, bytecode_t *code, function_code_t *func_code,
670         scope_chain_t *scope_chain, jsdisp_t **ret)
671 {
672     FunctionInstance *function;
673     jsdisp_t *prototype;
674     HRESULT hres;
675
676     hres = create_object(ctx, NULL, &prototype);
677     if(FAILED(hres))
678         return hres;
679
680     hres = create_function(ctx, NULL, PROPF_CONSTR, FALSE, NULL, &function);
681     if(SUCCEEDED(hres)) {
682         hres = set_prototype(ctx, &function->dispex, prototype);
683         if(SUCCEEDED(hres))
684             hres = set_constructor_prop(ctx, &function->dispex, prototype);
685         if(FAILED(hres))
686             jsdisp_release(&function->dispex);
687     }
688     jsdisp_release(prototype);
689     if(FAILED(hres))
690         return hres;
691
692     if(scope_chain) {
693         scope_addref(scope_chain);
694         function->scope_chain = scope_chain;
695     }
696
697     bytecode_addref(code);
698     function->code = code;
699     function->func_code = func_code;
700     function->length = function->func_code->param_cnt;
701
702     *ret = &function->dispex;
703     return S_OK;
704 }
705
706 static HRESULT construct_function(script_ctx_t *ctx, unsigned argc, jsval_t *argv, jsexcept_t *ei, IDispatch **ret)
707 {
708     WCHAR *str = NULL, *ptr;
709     DWORD len = 0, l;
710     bytecode_t *code;
711     jsdisp_t *function;
712     BSTR *params = NULL;
713     int i=0, j=0;
714     HRESULT hres = S_OK;
715
716     static const WCHAR function_anonymousW[] = {'f','u','n','c','t','i','o','n',' ','a','n','o','n','y','m','o','u','s','('};
717     static const WCHAR function_beginW[] = {')',' ','{','\n'};
718     static const WCHAR function_endW[] = {'\n','}',0};
719
720     if(argc) {
721         params = heap_alloc(argc*sizeof(BSTR));
722         if(!params)
723             return E_OUTOFMEMORY;
724
725         if(argc > 2)
726             len = (argc-2)*2; /* separating commas */
727         for(i=0; i < argc; i++) {
728             hres = to_string_jsval(ctx, argv[i], ei, params+i);
729             if(FAILED(hres))
730                 break;
731             len += SysStringLen(params[i]);
732         }
733     }
734
735     if(SUCCEEDED(hres)) {
736         len += (sizeof(function_anonymousW) + sizeof(function_beginW) + sizeof(function_endW)) / sizeof(WCHAR);
737         str = heap_alloc(len*sizeof(WCHAR));
738         if(str) {
739             memcpy(str, function_anonymousW, sizeof(function_anonymousW));
740             ptr = str + sizeof(function_anonymousW)/sizeof(WCHAR);
741             if(argc > 1) {
742                 while(1) {
743                     l = SysStringLen(params[j]);
744                     memcpy(ptr, params[j], l*sizeof(WCHAR));
745                     ptr += l;
746                     if(++j == argc-1)
747                         break;
748                     *ptr++ = ',';
749                     *ptr++ = ' ';
750                 }
751             }
752             memcpy(ptr, function_beginW, sizeof(function_beginW));
753             ptr += sizeof(function_beginW)/sizeof(WCHAR);
754             if(argc) {
755                 l = SysStringLen(params[argc-1]);
756                 memcpy(ptr, params[argc-1], l*sizeof(WCHAR));
757                 ptr += l;
758             }
759             memcpy(ptr, function_endW, sizeof(function_endW));
760
761             TRACE("%s\n", debugstr_w(str));
762         }else {
763             hres = E_OUTOFMEMORY;
764         }
765     }
766
767     while(--i >= 0)
768         SysFreeString(params[i]);
769     heap_free(params);
770     if(FAILED(hres))
771         return hres;
772
773     hres = compile_script(ctx, str, NULL, FALSE, FALSE, &code);
774     heap_free(str);
775     if(FAILED(hres))
776         return hres;
777
778     if(code->global_code.func_cnt != 1 || code->global_code.var_cnt) {
779         ERR("Invalid parser result!\n");
780         release_bytecode(code);
781         return E_UNEXPECTED;
782     }
783
784     hres = create_source_function(ctx, code, code->global_code.funcs, NULL, &function);
785     release_bytecode(code);
786     if(FAILED(hres))
787         return hres;
788
789     *ret = to_disp(function);
790     return S_OK;
791 }
792
793 static HRESULT FunctionConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
794         jsval_t *r, jsexcept_t *ei)
795 {
796     HRESULT hres;
797
798     TRACE("\n");
799
800     switch(flags) {
801     case DISPATCH_CONSTRUCT: {
802         IDispatch *ret;
803
804         hres = construct_function(ctx, argc, argv, ei, &ret);
805         if(FAILED(hres))
806             return hres;
807
808         *r = jsval_disp(ret);
809         break;
810     }
811     default:
812         FIXME("unimplemented flags %x\n", flags);
813         return E_NOTIMPL;
814     }
815
816     return S_OK;
817 }
818
819 static HRESULT FunctionProt_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
820         jsval_t *r, jsexcept_t *ei)
821 {
822     FIXME("\n");
823     return E_NOTIMPL;
824 }
825
826 HRESULT init_function_constr(script_ctx_t *ctx, jsdisp_t *object_prototype)
827 {
828     FunctionInstance *prot, *constr;
829     HRESULT hres;
830
831     static const WCHAR FunctionW[] = {'F','u','n','c','t','i','o','n',0};
832
833     hres = create_function(ctx, &Function_info, PROPF_CONSTR, TRUE, object_prototype, &prot);
834     if(FAILED(hres))
835         return hres;
836
837     prot->value_proc = FunctionProt_value;
838     prot->name = prototypeW;
839
840     hres = create_function(ctx, &FunctionInst_info, PROPF_CONSTR|1, TRUE, &prot->dispex, &constr);
841     if(SUCCEEDED(hres)) {
842         constr->value_proc = FunctionConstr_value;
843         constr->name = FunctionW;
844         hres = set_prototype(ctx, &constr->dispex, &prot->dispex);
845         if(SUCCEEDED(hres))
846             hres = set_constructor_prop(ctx, &constr->dispex, &prot->dispex);
847         if(FAILED(hres))
848             jsdisp_release(&constr->dispex);
849     }
850     jsdisp_release(&prot->dispex);
851     if(FAILED(hres))
852         return hres;
853
854     ctx->function_constr = &constr->dispex;
855     return S_OK;
856 }