xref: /reactos/dll/win32/jscript/function.c (revision 53221834)
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 _function_vtbl_t function_vtbl_t;
29 
30 typedef struct {
31     jsdisp_t dispex;
32     const function_vtbl_t *vtbl;
33     DWORD flags;
34     DWORD length;
35 } FunctionInstance;
36 
37 struct _function_vtbl_t {
38     HRESULT (*call)(script_ctx_t*,FunctionInstance*,IDispatch*,unsigned,unsigned,jsval_t*,jsval_t*);
39     HRESULT (*toString)(FunctionInstance*,jsstr_t**);
40     void (*destructor)(FunctionInstance*);
41 };
42 
43 typedef struct {
44     FunctionInstance function;
45     scope_chain_t *scope_chain;
46     bytecode_t *code;
47     function_code_t *func_code;
48 } InterpretedFunction;
49 
50 typedef struct {
51     FunctionInstance function;
52     builtin_invoke_t proc;
53     const WCHAR *name;
54 } NativeFunction;
55 
56 typedef struct {
57     FunctionInstance function;
58     FunctionInstance *target;
59     IDispatch *this;
60     unsigned argc;
61     jsval_t args[1];
62 } BindFunction;
63 
64 typedef struct {
65     jsdisp_t jsdisp;
66     InterpretedFunction *function;
67     jsval_t *buf;
68     call_frame_t *frame;
69     unsigned argc;
70 } ArgumentsInstance;
71 
72 static HRESULT create_bind_function(script_ctx_t*,FunctionInstance*,IDispatch*,unsigned,jsval_t*,jsdisp_t**r);
73 
74 static inline FunctionInstance *function_from_jsdisp(jsdisp_t *jsdisp)
75 {
76     return CONTAINING_RECORD(jsdisp, FunctionInstance, dispex);
77 }
78 
79 static inline FunctionInstance *function_from_vdisp(vdisp_t *vdisp)
80 {
81     return function_from_jsdisp(vdisp->u.jsdisp);
82 }
83 
84 static inline FunctionInstance *function_this(vdisp_t *jsthis)
85 {
86     return is_vclass(jsthis, JSCLASS_FUNCTION) ? function_from_vdisp(jsthis) : NULL;
87 }
88 
89 static inline ArgumentsInstance *arguments_from_jsdisp(jsdisp_t *jsdisp)
90 {
91     return CONTAINING_RECORD(jsdisp, ArgumentsInstance, jsdisp);
92 }
93 
94 static const WCHAR prototypeW[] = {'p','r','o','t','o','t', 'y', 'p','e',0};
95 
96 static const WCHAR lengthW[] = {'l','e','n','g','t','h',0};
97 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
98 static const WCHAR applyW[] = {'a','p','p','l','y',0};
99 static const WCHAR bindW[] = {'b','i','n','d',0};
100 static const WCHAR callW[] = {'c','a','l','l',0};
101 static const WCHAR argumentsW[] = {'a','r','g','u','m','e','n','t','s',0};
102 
103 static HRESULT Arguments_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
104         jsval_t *r)
105 {
106     FIXME("\n");
107     return E_NOTIMPL;
108 }
109 
110 static void Arguments_destructor(jsdisp_t *jsdisp)
111 {
112     ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
113 
114     TRACE("(%p)\n", arguments);
115 
116     if(arguments->buf) {
117         unsigned i;
118         for(i = 0; i < arguments->argc; i++)
119             jsval_release(arguments->buf[i]);
120         heap_free(arguments->buf);
121     }
122 
123     jsdisp_release(&arguments->function->function.dispex);
124     heap_free(arguments);
125 }
126 
127 static unsigned Arguments_idx_length(jsdisp_t *jsdisp)
128 {
129     ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
130     return arguments->argc;
131 }
132 
133 static jsval_t *get_argument_ref(ArgumentsInstance *arguments, unsigned idx)
134 {
135     if(arguments->buf)
136         return arguments->buf + idx;
137     if(arguments->frame->base_scope->frame || idx >= arguments->frame->function->param_cnt)
138         return arguments->jsdisp.ctx->stack + arguments->frame->arguments_off + idx;
139     return NULL;
140 }
141 
142 static HRESULT Arguments_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r)
143 {
144     ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
145     jsval_t *ref;
146 
147     TRACE("%p[%u]\n", arguments, idx);
148 
149     if((ref = get_argument_ref(arguments, idx)))
150         return jsval_copy(*ref, r);
151 
152     /* FIXME: Accessing by name won't work for duplicated argument names */
153     return jsdisp_propget_name(arguments->frame->base_scope->jsobj,
154                                arguments->function->func_code->params[idx], r);
155 }
156 
157 static HRESULT Arguments_idx_put(jsdisp_t *jsdisp, unsigned idx, jsval_t val)
158 {
159     ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
160     jsval_t *ref;
161     HRESULT hres;
162 
163     TRACE("%p[%u] = %s\n", arguments, idx, debugstr_jsval(val));
164 
165     if((ref = get_argument_ref(arguments, idx))) {
166         jsval_t copy;
167         hres = jsval_copy(val, &copy);
168         if(FAILED(hres))
169             return hres;
170 
171         jsval_release(*ref);
172         *ref = copy;
173         return S_OK;
174     }
175 
176     /* FIXME: Accessing by name won't work for duplicated argument names */
177     return jsdisp_propput_name(arguments->frame->base_scope->jsobj,
178                                arguments->function->func_code->params[idx], val);
179 }
180 
181 static const builtin_info_t Arguments_info = {
182     JSCLASS_ARGUMENTS,
183     {NULL, Arguments_value, 0},
184     0, NULL,
185     Arguments_destructor,
186     NULL,
187     Arguments_idx_length,
188     Arguments_idx_get,
189     Arguments_idx_put
190 };
191 
192 HRESULT setup_arguments_object(script_ctx_t *ctx, call_frame_t *frame)
193 {
194     ArgumentsInstance *args;
195     HRESULT hres;
196 
197     static const WCHAR caleeW[] = {'c','a','l','l','e','e',0};
198 
199     args = heap_alloc_zero(sizeof(*args));
200     if(!args)
201         return E_OUTOFMEMORY;
202 
203     hres = init_dispex_from_constr(&args->jsdisp, ctx, &Arguments_info, ctx->object_constr);
204     if(FAILED(hres)) {
205         heap_free(args);
206         return hres;
207     }
208 
209     args->function = (InterpretedFunction*)function_from_jsdisp(jsdisp_addref(frame->function_instance));
210     args->argc = frame->argc;
211     args->frame = frame;
212 
213     hres = jsdisp_define_data_property(&args->jsdisp, lengthW, PROPF_WRITABLE | PROPF_CONFIGURABLE,
214                                        jsval_number(args->argc));
215     if(SUCCEEDED(hres))
216         hres = jsdisp_define_data_property(&args->jsdisp, caleeW, PROPF_WRITABLE | PROPF_CONFIGURABLE,
217                                            jsval_obj(&args->function->function.dispex));
218     if(SUCCEEDED(hres))
219         hres = jsdisp_propput(frame->base_scope->jsobj, argumentsW, PROPF_WRITABLE, jsval_obj(&args->jsdisp));
220     if(FAILED(hres)) {
221         jsdisp_release(&args->jsdisp);
222         return hres;
223     }
224 
225     frame->arguments_obj = &args->jsdisp;
226     return S_OK;
227 }
228 
229 void detach_arguments_object(jsdisp_t *args_disp)
230 {
231     ArgumentsInstance *arguments = arguments_from_jsdisp(args_disp);
232     call_frame_t *frame = arguments->frame;
233     const BOOL on_stack = frame->base_scope->frame == frame;
234     HRESULT hres;
235 
236     /* Reset arguments value to cut the reference cycle. Note that since all activation contexts have
237      * their own arguments property, it's impossible to use prototype's one during name lookup */
238     jsdisp_propput_name(frame->base_scope->jsobj, argumentsW, jsval_undefined());
239     arguments->frame = NULL;
240 
241     /* Don't bother coppying arguments if call frame holds the last reference. */
242     if(arguments->jsdisp.ref > 1) {
243         arguments->buf = heap_alloc(arguments->argc * sizeof(*arguments->buf));
244         if(arguments->buf) {
245             int i;
246 
247             for(i = 0; i < arguments->argc ; i++) {
248                 if(on_stack || i >= frame->function->param_cnt)
249                     hres = jsval_copy(arguments->jsdisp.ctx->stack[frame->arguments_off + i], arguments->buf+i);
250                 else
251                     hres = jsdisp_propget_name(frame->base_scope->jsobj, frame->function->params[i], arguments->buf+i);
252                 if(FAILED(hres))
253                     arguments->buf[i] = jsval_undefined();
254             }
255         }else {
256             ERR("out of memory\n");
257             arguments->argc = 0;
258         }
259     }
260 
261     jsdisp_release(frame->arguments_obj);
262 }
263 
264 HRESULT Function_invoke(jsdisp_t *func_this, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
265 {
266     FunctionInstance *function;
267 
268     TRACE("func %p this %p\n", func_this, jsthis);
269 
270     assert(is_class(func_this, JSCLASS_FUNCTION));
271     function = function_from_jsdisp(func_this);
272 
273     return function->vtbl->call(function->dispex.ctx, function, jsthis, flags, argc, argv, r);
274 }
275 
276 static HRESULT Function_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
277 {
278     TRACE("%p\n", jsthis);
279 
280     *r = jsval_number(function_from_jsdisp(jsthis)->length);
281     return S_OK;
282 }
283 
284 static HRESULT Function_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
285         jsval_t *r)
286 {
287     FunctionInstance *function;
288     jsstr_t *str;
289     HRESULT hres;
290 
291     TRACE("\n");
292 
293     if(!(function = function_this(jsthis)))
294         return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
295 
296     hres = function->vtbl->toString(function, &str);
297     if(FAILED(hres))
298         return hres;
299 
300     if(r)
301         *r = jsval_string(str);
302     else
303         jsstr_release(str);
304     return S_OK;
305 }
306 
307 static HRESULT array_to_args(script_ctx_t *ctx, jsdisp_t *arg_array, unsigned *argc, jsval_t **ret)
308 {
309     jsval_t *argv, val;
310     DWORD length, i;
311     HRESULT hres;
312 
313     hres = jsdisp_propget_name(arg_array, lengthW, &val);
314     if(FAILED(hres))
315         return hres;
316 
317     hres = to_uint32(ctx, val, &length);
318     jsval_release(val);
319     if(FAILED(hres))
320         return hres;
321 
322     argv = heap_alloc(length * sizeof(*argv));
323     if(!argv)
324         return E_OUTOFMEMORY;
325 
326     for(i=0; i<length; i++) {
327         hres = jsdisp_get_idx(arg_array, i, argv+i);
328         if(hres == DISP_E_UNKNOWNNAME) {
329             argv[i] = jsval_undefined();
330         }else if(FAILED(hres)) {
331             while(i--)
332                 jsval_release(argv[i]);
333             heap_free(argv);
334             return hres;
335         }
336     }
337 
338     *argc = length;
339     *ret = argv;
340     return S_OK;
341 }
342 
343 static HRESULT Function_apply(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
344 {
345     FunctionInstance *function;
346     jsval_t *args = NULL;
347     unsigned i, cnt = 0;
348     IDispatch *this_obj = NULL;
349     HRESULT hres = S_OK;
350 
351     TRACE("\n");
352 
353     if(!(function = function_this(jsthis)) && (jsthis->flags & VDISP_JSDISP))
354         return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
355 
356     if(argc) {
357         if(!is_undefined(argv[0]) && !is_null(argv[0])) {
358             hres = to_object(ctx, argv[0], &this_obj);
359             if(FAILED(hres))
360                 return hres;
361         }
362     }
363 
364     if(argc >= 2) {
365         jsdisp_t *arg_array = NULL;
366 
367         if(is_object_instance(argv[1])) {
368             arg_array = iface_to_jsdisp(get_object(argv[1]));
369             if(arg_array &&
370                (!is_class(arg_array, JSCLASS_ARRAY) && !is_class(arg_array, JSCLASS_ARGUMENTS) )) {
371                 jsdisp_release(arg_array);
372                 arg_array = NULL;
373             }
374         }
375 
376         if(arg_array) {
377             hres = array_to_args(ctx, arg_array, &cnt, &args);
378             jsdisp_release(arg_array);
379         }else {
380             FIXME("throw TypeError\n");
381             hres = E_FAIL;
382         }
383     }
384 
385     if(SUCCEEDED(hres)) {
386         if(function) {
387             hres = function->vtbl->call(ctx, function, this_obj, flags, cnt, args, r);
388         }else {
389             jsval_t res;
390             hres = disp_call_value(ctx, jsthis->u.disp, this_obj, DISPATCH_METHOD, cnt, args, &res);
391             if(SUCCEEDED(hres)) {
392                 if(r)
393                     *r = res;
394                 else
395                     jsval_release(res);
396             }
397         }
398     }
399 
400     if(this_obj)
401         IDispatch_Release(this_obj);
402     for(i=0; i < cnt; i++)
403         jsval_release(args[i]);
404     heap_free(args);
405     return hres;
406 }
407 
408 static HRESULT Function_call(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
409         jsval_t *r)
410 {
411     FunctionInstance *function;
412     IDispatch *this_obj = NULL;
413     unsigned cnt = 0;
414     HRESULT hres;
415 
416     TRACE("\n");
417 
418     if(!(function = function_this(jsthis)))
419         return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
420 
421     if(argc) {
422         if(!is_undefined(argv[0]) && !is_null(argv[0])) {
423             hres = to_object(ctx, argv[0], &this_obj);
424             if(FAILED(hres))
425                 return hres;
426         }
427 
428         cnt = argc-1;
429     }
430 
431     hres = function->vtbl->call(ctx, function, this_obj, flags, cnt, argv + 1, r);
432 
433     if(this_obj)
434         IDispatch_Release(this_obj);
435     return hres;
436 }
437 
438 static HRESULT Function_bind(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
439         jsval_t *r)
440 {
441     FunctionInstance *function;
442     jsdisp_t *new_function;
443     HRESULT hres;
444 
445     TRACE("\n");
446 
447     if(!(function = function_this(jsthis)))
448         return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
449 
450     if(argc < 1) {
451         FIXME("no this argument\n");
452         return E_NOTIMPL;
453     }
454 
455     if(!is_object_instance(argv[0]) || !get_object(argv[0])) {
456         FIXME("%s is not an object instance\n", debugstr_jsval(argv[0]));
457         return E_NOTIMPL;
458     }
459 
460     hres = create_bind_function(ctx, function, get_object(argv[0]), argc - 1, argv + 1, &new_function);
461     if(FAILED(hres))
462         return hres;
463 
464     if(r)
465         *r = jsval_obj(new_function);
466     else
467         jsdisp_release(new_function);
468     return S_OK;
469 }
470 
471 HRESULT Function_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
472         jsval_t *r)
473 {
474     FunctionInstance *function;
475 
476     TRACE("\n");
477 
478     if(!is_vclass(jsthis, JSCLASS_FUNCTION)) {
479         ERR("dispex is not a function\n");
480         return E_FAIL;
481     }
482 
483     function = function_from_jsdisp(jsthis->u.jsdisp);
484     return function->vtbl->call(ctx, function, NULL, flags, argc, argv, r);
485 }
486 
487 HRESULT Function_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
488 {
489     FunctionInstance *function = function_from_jsdisp(jsthis);
490     jsstr_t *str;
491     HRESULT hres;
492 
493     TRACE("\n");
494 
495     hres = function->vtbl->toString(function, &str);
496     if(FAILED(hres))
497         return hres;
498 
499     *r = jsval_string(str);
500     return S_OK;
501 }
502 
503 static HRESULT Function_get_arguments(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
504 {
505     FunctionInstance *function = function_from_jsdisp(jsthis);
506     call_frame_t *frame;
507     HRESULT hres;
508 
509     TRACE("\n");
510 
511     for(frame = ctx->call_ctx; frame; frame = frame->prev_frame) {
512         if(frame->function_instance == &function->dispex) {
513             if(!frame->arguments_obj) {
514                 hres = setup_arguments_object(ctx, frame);
515                 if(FAILED(hres))
516                     return hres;
517             }
518             *r = jsval_obj(jsdisp_addref(frame->arguments_obj));
519             return S_OK;
520         }
521     }
522 
523     *r = jsval_null();
524     return S_OK;
525 }
526 
527 static void Function_destructor(jsdisp_t *dispex)
528 {
529     FunctionInstance *function = function_from_jsdisp(dispex);
530     function->vtbl->destructor(function);
531     heap_free(function);
532 }
533 
534 static const builtin_prop_t Function_props[] = {
535     {applyW,                 Function_apply,                 PROPF_METHOD|2},
536     {argumentsW,             NULL, 0,                        Function_get_arguments},
537     {bindW,                  Function_bind,                  PROPF_METHOD|PROPF_ES5|1},
538     {callW,                  Function_call,                  PROPF_METHOD|1},
539     {lengthW,                NULL, 0,                        Function_get_length},
540     {toStringW,              Function_toString,              PROPF_METHOD}
541 };
542 
543 static const builtin_info_t Function_info = {
544     JSCLASS_FUNCTION,
545     DEFAULT_FUNCTION_VALUE,
546     ARRAY_SIZE(Function_props),
547     Function_props,
548     Function_destructor,
549     NULL
550 };
551 
552 static const builtin_prop_t FunctionInst_props[] = {
553     {argumentsW,             NULL, 0,                        Function_get_arguments},
554     {lengthW,                NULL, 0,                        Function_get_length}
555 };
556 
557 static const builtin_info_t FunctionInst_info = {
558     JSCLASS_FUNCTION,
559     DEFAULT_FUNCTION_VALUE,
560     ARRAY_SIZE(FunctionInst_props),
561     FunctionInst_props,
562     Function_destructor,
563     NULL
564 };
565 
566 static HRESULT create_function(script_ctx_t *ctx, const builtin_info_t *builtin_info, const function_vtbl_t *vtbl, size_t size,
567         DWORD flags, BOOL funcprot, jsdisp_t *prototype, void **ret)
568 {
569     FunctionInstance *function;
570     HRESULT hres;
571 
572     function = heap_alloc_zero(size);
573     if(!function)
574         return E_OUTOFMEMORY;
575 
576     if(funcprot)
577         hres = init_dispex(&function->dispex, ctx, builtin_info, prototype);
578     else if(builtin_info)
579         hres = init_dispex_from_constr(&function->dispex, ctx, builtin_info, ctx->function_constr);
580     else
581         hres = init_dispex_from_constr(&function->dispex, ctx, &FunctionInst_info, ctx->function_constr);
582     if(FAILED(hres)) {
583         heap_free(function);
584         return hres;
585     }
586 
587     function->vtbl = vtbl;
588     function->flags = flags;
589     function->length = flags & PROPF_ARGMASK;
590 
591     *ret = function;
592     return S_OK;
593 }
594 
595 static HRESULT NativeFunction_call(script_ctx_t *ctx, FunctionInstance *func, IDispatch *this_disp, unsigned flags,
596         unsigned argc, jsval_t *argv, jsval_t *r)
597 {
598     NativeFunction *function = (NativeFunction*)func;
599     vdisp_t vthis;
600     HRESULT hres;
601 
602     if(this_disp)
603         set_disp(&vthis, this_disp);
604     else if(ctx->host_global)
605         set_disp(&vthis, ctx->host_global);
606     else
607         set_jsdisp(&vthis, ctx->global);
608 
609     hres = function->proc(ctx, &vthis, flags & ~DISPATCH_JSCRIPT_INTERNAL_MASK, argc, argv, r);
610 
611     vdisp_release(&vthis);
612     return hres;
613 }
614 
615 static HRESULT NativeFunction_toString(FunctionInstance *func, jsstr_t **ret)
616 {
617     NativeFunction *function = (NativeFunction*)func;
618     DWORD name_len;
619     jsstr_t *str;
620     WCHAR *ptr;
621 
622     static const WCHAR native_prefixW[] = {'\n','f','u','n','c','t','i','o','n',' '};
623     static const WCHAR native_suffixW[] =
624         {'(',')',' ','{','\n',' ',' ',' ',' ','[','n','a','t','i','v','e',' ','c','o','d','e',']','\n','}','\n'};
625 
626     name_len = function->name ? lstrlenW(function->name) : 0;
627     str = jsstr_alloc_buf(ARRAY_SIZE(native_prefixW) + ARRAY_SIZE(native_suffixW) + name_len, &ptr);
628     if(!str)
629         return E_OUTOFMEMORY;
630 
631     memcpy(ptr, native_prefixW, sizeof(native_prefixW));
632     ptr += ARRAY_SIZE(native_prefixW);
633     memcpy(ptr, function->name, name_len*sizeof(WCHAR));
634     ptr += name_len;
635     memcpy(ptr, native_suffixW, sizeof(native_suffixW));
636 
637     *ret = str;
638     return S_OK;
639 }
640 
641 static void NativeFunction_destructor(FunctionInstance *function)
642 {
643 }
644 
645 static const function_vtbl_t NativeFunctionVtbl = {
646     NativeFunction_call,
647     NativeFunction_toString,
648     NativeFunction_destructor
649 };
650 
651 HRESULT create_builtin_function(script_ctx_t *ctx, builtin_invoke_t value_proc, const WCHAR *name,
652         const builtin_info_t *builtin_info, DWORD flags, jsdisp_t *prototype, jsdisp_t **ret)
653 {
654     NativeFunction *function;
655     HRESULT hres;
656 
657     hres = create_function(ctx, builtin_info, &NativeFunctionVtbl, sizeof(NativeFunction), flags, FALSE, NULL, (void**)&function);
658     if(FAILED(hres))
659         return hres;
660 
661     if(builtin_info)
662         hres = jsdisp_define_data_property(&function->function.dispex, lengthW, 0,
663                                            jsval_number(function->function.length));
664     if(SUCCEEDED(hres))
665         hres = jsdisp_define_data_property(&function->function.dispex, prototypeW, 0, jsval_obj(prototype));
666     if(FAILED(hres)) {
667         jsdisp_release(&function->function.dispex);
668         return hres;
669     }
670 
671     function->proc = value_proc;
672     function->name = name;
673 
674     *ret = &function->function.dispex;
675     return S_OK;
676 }
677 
678 static HRESULT set_constructor_prop(script_ctx_t *ctx, jsdisp_t *constr, jsdisp_t *prot)
679 {
680     static const WCHAR constructorW[] = {'c','o','n','s','t','r','u','c','t','o','r',0};
681 
682     return jsdisp_define_data_property(prot, constructorW, PROPF_WRITABLE | PROPF_CONFIGURABLE,
683                                        jsval_obj(constr));
684 }
685 
686 HRESULT create_builtin_constructor(script_ctx_t *ctx, builtin_invoke_t value_proc, const WCHAR *name,
687         const builtin_info_t *builtin_info, DWORD flags, jsdisp_t *prototype, jsdisp_t **ret)
688 {
689     jsdisp_t *constr;
690     HRESULT hres;
691 
692     hres = create_builtin_function(ctx, value_proc, name, builtin_info, flags, prototype, &constr);
693     if(FAILED(hres))
694         return hres;
695 
696     hres = set_constructor_prop(ctx, constr, prototype);
697     if(FAILED(hres)) {
698         jsdisp_release(constr);
699         return hres;
700     }
701 
702     *ret = constr;
703     return S_OK;
704 }
705 
706 static HRESULT InterpretedFunction_call(script_ctx_t *ctx, FunctionInstance *func, IDispatch *this_obj, unsigned flags,
707          unsigned argc, jsval_t *argv, jsval_t *r)
708 {
709     InterpretedFunction *function = (InterpretedFunction*)func;
710     jsdisp_t *var_disp, *new_obj = NULL;
711     DWORD exec_flags = 0;
712     HRESULT hres;
713 
714     TRACE("%p\n", function);
715 
716     if(ctx->state == SCRIPTSTATE_UNINITIALIZED || ctx->state == SCRIPTSTATE_CLOSED) {
717         WARN("Script engine state does not allow running code.\n");
718         return E_UNEXPECTED;
719     }
720 
721     if(flags & DISPATCH_CONSTRUCT) {
722         hres = create_object(ctx, &function->function.dispex, &new_obj);
723         if(FAILED(hres))
724             return hres;
725         this_obj = to_disp(new_obj);
726     }
727 
728     if(flags & DISPATCH_JSCRIPT_CALLEREXECSSOURCE)
729         exec_flags |= EXEC_RETURN_TO_INTERP;
730     if(flags & DISPATCH_CONSTRUCT)
731         exec_flags |= EXEC_CONSTRUCTOR;
732 
733     hres = create_dispex(ctx, NULL, NULL, &var_disp);
734     if(SUCCEEDED(hres))
735         hres = exec_source(ctx, exec_flags, function->code, function->func_code, function->scope_chain, this_obj,
736                            &function->function.dispex, var_disp, argc, argv, r);
737     if(new_obj)
738         jsdisp_release(new_obj);
739 
740     jsdisp_release(var_disp);
741     return hres;
742 }
743 
744 static HRESULT InterpretedFunction_toString(FunctionInstance *func, jsstr_t **ret)
745 {
746     InterpretedFunction *function = (InterpretedFunction*)func;
747 
748     *ret = jsstr_alloc_len(function->func_code->source, function->func_code->source_len);
749     return *ret ? S_OK : E_OUTOFMEMORY;
750 }
751 
752 static void InterpretedFunction_destructor(FunctionInstance *func)
753 {
754     InterpretedFunction *function = (InterpretedFunction*)func;
755 
756     release_bytecode(function->code);
757     if(function->scope_chain)
758         scope_release(function->scope_chain);
759 }
760 
761 static const function_vtbl_t InterpretedFunctionVtbl = {
762     InterpretedFunction_call,
763     InterpretedFunction_toString,
764     InterpretedFunction_destructor
765 };
766 
767 HRESULT create_source_function(script_ctx_t *ctx, bytecode_t *code, function_code_t *func_code,
768         scope_chain_t *scope_chain, jsdisp_t **ret)
769 {
770     InterpretedFunction *function;
771     jsdisp_t *prototype;
772     HRESULT hres;
773 
774     hres = create_object(ctx, NULL, &prototype);
775     if(FAILED(hres))
776         return hres;
777 
778     hres = create_function(ctx, NULL, &InterpretedFunctionVtbl, sizeof(InterpretedFunction), PROPF_CONSTR,
779                            FALSE, NULL, (void**)&function);
780     if(SUCCEEDED(hres)) {
781         hres = jsdisp_define_data_property(&function->function.dispex, prototypeW, PROPF_WRITABLE,
782                                            jsval_obj(prototype));
783         if(SUCCEEDED(hres))
784             hres = set_constructor_prop(ctx, &function->function.dispex, prototype);
785         if(FAILED(hres))
786             jsdisp_release(&function->function.dispex);
787     }
788     jsdisp_release(prototype);
789     if(FAILED(hres))
790         return hres;
791 
792     if(scope_chain) {
793         scope_addref(scope_chain);
794         function->scope_chain = scope_chain;
795     }
796 
797     bytecode_addref(code);
798     function->code = code;
799     function->func_code = func_code;
800     function->function.length = function->func_code->param_cnt;
801 
802     *ret = &function->function.dispex;
803     return S_OK;
804 }
805 
806 static HRESULT BindFunction_call(script_ctx_t *ctx, FunctionInstance *func, IDispatch *this_obj, unsigned flags,
807          unsigned argc, jsval_t *argv, jsval_t *r)
808 {
809     BindFunction *function = (BindFunction*)func;
810     jsval_t *call_args = NULL;
811     unsigned call_argc;
812     HRESULT hres;
813 
814     TRACE("%p\n", function);
815 
816     call_argc = function->argc + argc;
817     if(call_argc) {
818         call_args = heap_alloc(function->argc * sizeof(*function->args));
819         if(!call_args)
820             return E_OUTOFMEMORY;
821 
822         if(function->argc)
823             memcpy(call_args, function->args, function->argc * sizeof(*call_args));
824         if(argc)
825             memcpy(call_args + function->argc, argv, argc * sizeof(*call_args));
826     }
827 
828     hres = function->target->vtbl->call(ctx, function->target, function->this, flags, call_argc, call_args, r);
829 
830     heap_free(call_args);
831     return hres;
832 }
833 
834 static HRESULT BindFunction_toString(FunctionInstance *function, jsstr_t **ret)
835 {
836     static const WCHAR native_functionW[] =
837         {'\n','f','u','n','c','t','i','o','n','(',')',' ','{','\n',
838          ' ',' ',' ',' ','[','n','a','t','i','v','e',' ','c','o','d','e',']','\n',
839          '}','\n',0};
840 
841     *ret = jsstr_alloc(native_functionW);
842     return *ret ? S_OK : E_OUTOFMEMORY;
843 }
844 
845 static void BindFunction_destructor(FunctionInstance *func)
846 {
847     BindFunction *function = (BindFunction*)func;
848     unsigned i;
849 
850     TRACE("%p\n", function);
851 
852     for(i = 0; i < function->argc; i++)
853         jsval_release(function->args[i]);
854     jsdisp_release(&function->target->dispex);
855     IDispatch_Release(function->this);
856 }
857 
858 static const function_vtbl_t BindFunctionVtbl = {
859     BindFunction_call,
860     BindFunction_toString,
861     BindFunction_destructor
862 };
863 
864 static HRESULT create_bind_function(script_ctx_t *ctx, FunctionInstance *target, IDispatch *bound_this, unsigned argc,
865                                     jsval_t *argv, jsdisp_t **ret)
866 {
867     BindFunction *function;
868     HRESULT hres;
869 
870     hres = create_function(ctx, NULL, &BindFunctionVtbl, FIELD_OFFSET(BindFunction, args[argc]), PROPF_METHOD,
871                            FALSE, NULL, (void**)&function);
872     if(FAILED(hres))
873         return hres;
874 
875     jsdisp_addref(&target->dispex);
876     function->target = target;
877 
878     IDispatch_AddRef(function->this = bound_this);
879 
880     for(function->argc = 0; function->argc < argc; function->argc++) {
881         hres = jsval_copy(argv[function->argc], function->args + function->argc);
882         if(FAILED(hres)) {
883             jsdisp_release(&function->function.dispex);
884             return hres;
885         }
886     }
887 
888     function->function.length = target->length > argc ? target->length - argc : 0;
889 
890     *ret = &function->function.dispex;
891     return S_OK;
892 }
893 
894 static HRESULT construct_function(script_ctx_t *ctx, unsigned argc, jsval_t *argv, IDispatch **ret)
895 {
896     WCHAR *str = NULL, *ptr;
897     unsigned len = 0, i = 0;
898     bytecode_t *code;
899     jsdisp_t *function;
900     jsstr_t **params = NULL;
901     int j = 0;
902     HRESULT hres = S_OK;
903 
904     static const WCHAR function_anonymousW[] = {'f','u','n','c','t','i','o','n',' ','a','n','o','n','y','m','o','u','s','('};
905     static const WCHAR function_beginW[] = {')',' ','{','\n'};
906     static const WCHAR function_endW[] = {'\n','}',0};
907 
908     if(argc) {
909         params = heap_alloc(argc*sizeof(*params));
910         if(!params)
911             return E_OUTOFMEMORY;
912 
913         if(argc > 2)
914             len = (argc-2)*2; /* separating commas */
915         for(i=0; i < argc; i++) {
916             hres = to_string(ctx, argv[i], params+i);
917             if(FAILED(hres))
918                 break;
919             len += jsstr_length(params[i]);
920         }
921     }
922 
923     if(SUCCEEDED(hres)) {
924         len += ARRAY_SIZE(function_anonymousW) + ARRAY_SIZE(function_beginW) + ARRAY_SIZE(function_endW);
925         str = heap_alloc(len*sizeof(WCHAR));
926         if(str) {
927             memcpy(str, function_anonymousW, sizeof(function_anonymousW));
928             ptr = str + ARRAY_SIZE(function_anonymousW);
929             if(argc > 1) {
930                 while(1) {
931                     ptr += jsstr_flush(params[j], ptr);
932                     if(++j == argc-1)
933                         break;
934                     *ptr++ = ',';
935                     *ptr++ = ' ';
936                 }
937             }
938             memcpy(ptr, function_beginW, sizeof(function_beginW));
939             ptr += ARRAY_SIZE(function_beginW);
940             if(argc)
941                 ptr += jsstr_flush(params[argc-1], ptr);
942             memcpy(ptr, function_endW, sizeof(function_endW));
943 
944             TRACE("%s\n", debugstr_w(str));
945         }else {
946             hres = E_OUTOFMEMORY;
947         }
948     }
949 
950     while(i)
951         jsstr_release(params[--i]);
952     heap_free(params);
953     if(FAILED(hres))
954         return hres;
955 
956     hres = compile_script(ctx, str, NULL, NULL, FALSE, FALSE, &code);
957     heap_free(str);
958     if(FAILED(hres))
959         return hres;
960 
961     if(code->global_code.func_cnt != 1 || code->global_code.var_cnt != 1) {
962         ERR("Invalid parser result!\n");
963         release_bytecode(code);
964         return E_UNEXPECTED;
965     }
966 
967     hres = create_source_function(ctx, code, code->global_code.funcs, NULL, &function);
968     release_bytecode(code);
969     if(FAILED(hres))
970         return hres;
971 
972     *ret = to_disp(function);
973     return S_OK;
974 }
975 
976 static HRESULT FunctionConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
977         jsval_t *r)
978 {
979     HRESULT hres;
980 
981     TRACE("\n");
982 
983     switch(flags) {
984     case DISPATCH_METHOD:
985     case DISPATCH_CONSTRUCT: {
986         IDispatch *ret;
987 
988         hres = construct_function(ctx, argc, argv, &ret);
989         if(FAILED(hres))
990             return hres;
991 
992         *r = jsval_disp(ret);
993         break;
994     }
995     default:
996         FIXME("unimplemented flags %x\n", flags);
997         return E_NOTIMPL;
998     }
999 
1000     return S_OK;
1001 }
1002 
1003 static HRESULT FunctionProt_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1004         jsval_t *r)
1005 {
1006     FIXME("\n");
1007     return E_NOTIMPL;
1008 }
1009 
1010 HRESULT init_function_constr(script_ctx_t *ctx, jsdisp_t *object_prototype)
1011 {
1012     NativeFunction *prot, *constr;
1013     HRESULT hres;
1014 
1015     static const WCHAR FunctionW[] = {'F','u','n','c','t','i','o','n',0};
1016 
1017     hres = create_function(ctx, &Function_info, &NativeFunctionVtbl, sizeof(NativeFunction), PROPF_CONSTR,
1018                            TRUE, object_prototype, (void**)&prot);
1019     if(FAILED(hres))
1020         return hres;
1021 
1022     prot->proc = FunctionProt_value;
1023     prot->name = prototypeW;
1024 
1025     hres = create_function(ctx, &FunctionInst_info, &NativeFunctionVtbl, sizeof(NativeFunction), PROPF_CONSTR|1,
1026                            TRUE, &prot->function.dispex, (void**)&constr);
1027     if(SUCCEEDED(hres)) {
1028         constr->proc = FunctionConstr_value;
1029         constr->name = FunctionW;
1030         hres = jsdisp_define_data_property(&constr->function.dispex, prototypeW, 0, jsval_obj(&prot->function.dispex));
1031         if(SUCCEEDED(hres))
1032             hres = set_constructor_prop(ctx, &constr->function.dispex, &prot->function.dispex);
1033         if(FAILED(hres))
1034             jsdisp_release(&constr->function.dispex);
1035     }
1036     jsdisp_release(&prot->function.dispex);
1037     if(FAILED(hres))
1038         return hres;
1039 
1040     ctx->function_constr = &constr->function.dispex;
1041     return S_OK;
1042 }
1043