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