jscript: Support null this in Function.prototype.bind.
[wine.git] / dlls / jscript / function.c
blobe43d19464bb463cbb151887217e31700c7121254
1 /*
2 * Copyright 2008 Jacek Caban for CodeWeavers
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.
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.
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
19 #include <assert.h>
21 #include "jscript.h"
22 #include "engine.h"
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
28 typedef struct _function_vtbl_t function_vtbl_t;
30 typedef struct {
31 jsdisp_t dispex;
32 const function_vtbl_t *vtbl;
33 DWORD flags;
34 DWORD length;
35 } FunctionInstance;
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 function_code_t* (*get_code)(FunctionInstance*);
41 void (*destructor)(FunctionInstance*);
44 typedef struct {
45 FunctionInstance function;
46 scope_chain_t *scope_chain;
47 bytecode_t *code;
48 function_code_t *func_code;
49 } InterpretedFunction;
51 typedef struct {
52 FunctionInstance function;
53 builtin_invoke_t proc;
54 const WCHAR *name;
55 } NativeFunction;
57 typedef struct {
58 FunctionInstance function;
59 FunctionInstance *target;
60 IDispatch *this;
61 unsigned argc;
62 jsval_t args[1];
63 } BindFunction;
65 typedef struct {
66 jsdisp_t jsdisp;
67 InterpretedFunction *function;
68 jsval_t *buf;
69 call_frame_t *frame;
70 unsigned argc;
71 } ArgumentsInstance;
73 static HRESULT create_bind_function(script_ctx_t*,FunctionInstance*,IDispatch*,unsigned,jsval_t*,jsdisp_t**r);
75 static inline FunctionInstance *function_from_jsdisp(jsdisp_t *jsdisp)
77 return CONTAINING_RECORD(jsdisp, FunctionInstance, dispex);
80 static inline FunctionInstance *function_from_vdisp(vdisp_t *vdisp)
82 return function_from_jsdisp(vdisp->u.jsdisp);
85 static inline FunctionInstance *function_this(vdisp_t *jsthis)
87 return is_vclass(jsthis, JSCLASS_FUNCTION) ? function_from_vdisp(jsthis) : NULL;
90 static inline ArgumentsInstance *arguments_from_jsdisp(jsdisp_t *jsdisp)
92 return CONTAINING_RECORD(jsdisp, ArgumentsInstance, jsdisp);
95 static const WCHAR prototypeW[] = {'p','r','o','t','o','t', 'y', 'p','e',0};
97 static const WCHAR lengthW[] = {'l','e','n','g','t','h',0};
98 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
99 static const WCHAR applyW[] = {'a','p','p','l','y',0};
100 static const WCHAR bindW[] = {'b','i','n','d',0};
101 static const WCHAR callW[] = {'c','a','l','l',0};
102 static const WCHAR argumentsW[] = {'a','r','g','u','m','e','n','t','s',0};
104 static HRESULT Arguments_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
105 jsval_t *r)
107 FIXME("\n");
108 return E_NOTIMPL;
111 static void Arguments_destructor(jsdisp_t *jsdisp)
113 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
115 TRACE("(%p)\n", arguments);
117 if(arguments->buf) {
118 unsigned i;
119 for(i = 0; i < arguments->argc; i++)
120 jsval_release(arguments->buf[i]);
121 heap_free(arguments->buf);
124 jsdisp_release(&arguments->function->function.dispex);
125 heap_free(arguments);
128 static unsigned Arguments_idx_length(jsdisp_t *jsdisp)
130 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
131 return arguments->argc;
134 static jsval_t *get_argument_ref(ArgumentsInstance *arguments, unsigned idx)
136 if(arguments->buf)
137 return arguments->buf + idx;
138 if(arguments->frame->base_scope->frame || idx >= arguments->frame->function->param_cnt)
139 return arguments->jsdisp.ctx->stack + arguments->frame->arguments_off + idx;
140 return NULL;
143 static HRESULT Arguments_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r)
145 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
146 jsval_t *ref;
148 TRACE("%p[%u]\n", arguments, idx);
150 if((ref = get_argument_ref(arguments, idx)))
151 return jsval_copy(*ref, r);
153 /* FIXME: Accessing by name won't work for duplicated argument names */
154 return jsdisp_propget_name(arguments->frame->base_scope->jsobj,
155 arguments->function->func_code->params[idx], r);
158 static HRESULT Arguments_idx_put(jsdisp_t *jsdisp, unsigned idx, jsval_t val)
160 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
161 jsval_t *ref;
162 HRESULT hres;
164 TRACE("%p[%u] = %s\n", arguments, idx, debugstr_jsval(val));
166 if((ref = get_argument_ref(arguments, idx))) {
167 jsval_t copy;
168 hres = jsval_copy(val, &copy);
169 if(FAILED(hres))
170 return hres;
172 jsval_release(*ref);
173 *ref = copy;
174 return S_OK;
177 /* FIXME: Accessing by name won't work for duplicated argument names */
178 return jsdisp_propput_name(arguments->frame->base_scope->jsobj,
179 arguments->function->func_code->params[idx], val);
182 static const builtin_info_t Arguments_info = {
183 JSCLASS_ARGUMENTS,
184 {NULL, Arguments_value, 0},
185 0, NULL,
186 Arguments_destructor,
187 NULL,
188 Arguments_idx_length,
189 Arguments_idx_get,
190 Arguments_idx_put
193 HRESULT setup_arguments_object(script_ctx_t *ctx, call_frame_t *frame)
195 ArgumentsInstance *args;
196 HRESULT hres;
198 static const WCHAR caleeW[] = {'c','a','l','l','e','e',0};
200 args = heap_alloc_zero(sizeof(*args));
201 if(!args)
202 return E_OUTOFMEMORY;
204 hres = init_dispex_from_constr(&args->jsdisp, ctx, &Arguments_info, ctx->object_constr);
205 if(FAILED(hres)) {
206 heap_free(args);
207 return hres;
210 args->function = (InterpretedFunction*)function_from_jsdisp(jsdisp_addref(frame->function_instance));
211 args->argc = frame->argc;
212 args->frame = frame;
214 hres = jsdisp_define_data_property(&args->jsdisp, lengthW, PROPF_WRITABLE | PROPF_CONFIGURABLE,
215 jsval_number(args->argc));
216 if(SUCCEEDED(hres))
217 hres = jsdisp_define_data_property(&args->jsdisp, caleeW, PROPF_WRITABLE | PROPF_CONFIGURABLE,
218 jsval_obj(&args->function->function.dispex));
219 if(SUCCEEDED(hres))
220 hres = jsdisp_propput(frame->base_scope->jsobj, argumentsW, PROPF_WRITABLE, jsval_obj(&args->jsdisp));
221 if(FAILED(hres)) {
222 jsdisp_release(&args->jsdisp);
223 return hres;
226 frame->arguments_obj = &args->jsdisp;
227 return S_OK;
230 void detach_arguments_object(jsdisp_t *args_disp)
232 ArgumentsInstance *arguments = arguments_from_jsdisp(args_disp);
233 call_frame_t *frame = arguments->frame;
234 const BOOL on_stack = frame->base_scope->frame == frame;
235 HRESULT hres;
237 /* Reset arguments value to cut the reference cycle. Note that since all activation contexts have
238 * their own arguments property, it's impossible to use prototype's one during name lookup */
239 jsdisp_propput_name(frame->base_scope->jsobj, argumentsW, jsval_undefined());
240 arguments->frame = NULL;
242 /* Don't bother coppying arguments if call frame holds the last reference. */
243 if(arguments->jsdisp.ref > 1) {
244 arguments->buf = heap_alloc(arguments->argc * sizeof(*arguments->buf));
245 if(arguments->buf) {
246 int i;
248 for(i = 0; i < arguments->argc ; i++) {
249 if(on_stack || i >= frame->function->param_cnt)
250 hres = jsval_copy(arguments->jsdisp.ctx->stack[frame->arguments_off + i], arguments->buf+i);
251 else
252 hres = jsdisp_propget_name(frame->base_scope->jsobj, frame->function->params[i], arguments->buf+i);
253 if(FAILED(hres))
254 arguments->buf[i] = jsval_undefined();
256 }else {
257 ERR("out of memory\n");
258 arguments->argc = 0;
262 jsdisp_release(frame->arguments_obj);
265 HRESULT Function_invoke(jsdisp_t *func_this, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
267 FunctionInstance *function;
269 TRACE("func %p this %p\n", func_this, jsthis);
271 assert(is_class(func_this, JSCLASS_FUNCTION));
272 function = function_from_jsdisp(func_this);
274 return function->vtbl->call(function->dispex.ctx, function, jsthis, flags, argc, argv, r);
277 static HRESULT Function_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
279 TRACE("%p\n", jsthis);
281 *r = jsval_number(function_from_jsdisp(jsthis)->length);
282 return S_OK;
285 static HRESULT Function_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
286 jsval_t *r)
288 FunctionInstance *function;
289 jsstr_t *str;
290 HRESULT hres;
292 TRACE("\n");
294 if(!(function = function_this(jsthis)))
295 return JS_E_FUNCTION_EXPECTED;
297 hres = function->vtbl->toString(function, &str);
298 if(FAILED(hres))
299 return hres;
301 if(r)
302 *r = jsval_string(str);
303 else
304 jsstr_release(str);
305 return S_OK;
308 static HRESULT array_to_args(script_ctx_t *ctx, jsdisp_t *arg_array, unsigned *argc, jsval_t **ret)
310 jsval_t *argv, val;
311 DWORD length, i;
312 HRESULT hres;
314 hres = jsdisp_propget_name(arg_array, lengthW, &val);
315 if(FAILED(hres))
316 return hres;
318 hres = to_uint32(ctx, val, &length);
319 jsval_release(val);
320 if(FAILED(hres))
321 return hres;
323 argv = heap_alloc(length * sizeof(*argv));
324 if(!argv)
325 return E_OUTOFMEMORY;
327 for(i=0; i<length; i++) {
328 hres = jsdisp_get_idx(arg_array, i, argv+i);
329 if(hres == DISP_E_UNKNOWNNAME) {
330 argv[i] = jsval_undefined();
331 }else if(FAILED(hres)) {
332 while(i--)
333 jsval_release(argv[i]);
334 heap_free(argv);
335 return hres;
339 *argc = length;
340 *ret = argv;
341 return S_OK;
344 static HRESULT Function_apply(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
346 FunctionInstance *function;
347 jsval_t *args = NULL;
348 unsigned i, cnt = 0;
349 IDispatch *this_obj = NULL;
350 HRESULT hres = S_OK;
352 TRACE("\n");
354 if(!(function = function_this(jsthis)) && (jsthis->flags & VDISP_JSDISP))
355 return JS_E_FUNCTION_EXPECTED;
357 if(argc) {
358 if(!is_undefined(argv[0]) && !is_null(argv[0])) {
359 hres = to_object(ctx, argv[0], &this_obj);
360 if(FAILED(hres))
361 return hres;
365 if(argc >= 2) {
366 jsdisp_t *arg_array = NULL;
368 if(is_object_instance(argv[1])) {
369 arg_array = iface_to_jsdisp(get_object(argv[1]));
370 if(arg_array &&
371 (!is_class(arg_array, JSCLASS_ARRAY) && !is_class(arg_array, JSCLASS_ARGUMENTS) )) {
372 jsdisp_release(arg_array);
373 arg_array = NULL;
377 if(arg_array) {
378 hres = array_to_args(ctx, arg_array, &cnt, &args);
379 jsdisp_release(arg_array);
380 }else {
381 FIXME("throw TypeError\n");
382 hres = E_FAIL;
386 if(SUCCEEDED(hres)) {
387 if(function) {
388 hres = function->vtbl->call(ctx, function, this_obj, flags, cnt, args, r);
389 }else {
390 jsval_t res;
391 hres = disp_call_value(ctx, jsthis->u.disp, this_obj, DISPATCH_METHOD, cnt, args, &res);
392 if(SUCCEEDED(hres)) {
393 if(r)
394 *r = res;
395 else
396 jsval_release(res);
401 if(this_obj)
402 IDispatch_Release(this_obj);
403 for(i=0; i < cnt; i++)
404 jsval_release(args[i]);
405 heap_free(args);
406 return hres;
409 static HRESULT Function_call(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
410 jsval_t *r)
412 FunctionInstance *function;
413 IDispatch *this_obj = NULL;
414 unsigned cnt = 0;
415 HRESULT hres;
417 TRACE("\n");
419 if(!(function = function_this(jsthis)))
420 return JS_E_FUNCTION_EXPECTED;
422 if(argc) {
423 if(!is_undefined(argv[0]) && !is_null(argv[0])) {
424 hres = to_object(ctx, argv[0], &this_obj);
425 if(FAILED(hres))
426 return hres;
429 cnt = argc-1;
432 hres = function->vtbl->call(ctx, function, this_obj, flags, cnt, argv + 1, r);
434 if(this_obj)
435 IDispatch_Release(this_obj);
436 return hres;
439 static HRESULT Function_bind(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
440 jsval_t *r)
442 IDispatch *bound_this = NULL;
443 FunctionInstance *function;
444 jsdisp_t *new_function;
445 HRESULT hres;
447 TRACE("\n");
449 if(!(function = function_this(jsthis)))
450 return JS_E_FUNCTION_EXPECTED;
452 if(argc < 1) {
453 FIXME("no this argument\n");
454 return E_NOTIMPL;
457 if(is_object_instance(argv[0])) {
458 bound_this = get_object(argv[0]);
459 }else if(!is_null(argv[0])) {
460 FIXME("%s is not an object instance\n", debugstr_jsval(argv[0]));
461 return E_NOTIMPL;
464 hres = create_bind_function(ctx, function, bound_this, argc - 1, argv + 1, &new_function);
465 if(FAILED(hres))
466 return hres;
468 if(r)
469 *r = jsval_obj(new_function);
470 else
471 jsdisp_release(new_function);
472 return S_OK;
475 HRESULT Function_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
476 jsval_t *r)
478 FunctionInstance *function;
480 TRACE("\n");
482 if(!is_vclass(jsthis, JSCLASS_FUNCTION)) {
483 ERR("dispex is not a function\n");
484 return E_FAIL;
487 function = function_from_jsdisp(jsthis->u.jsdisp);
488 return function->vtbl->call(ctx, function, NULL, flags, argc, argv, r);
491 HRESULT Function_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
493 FunctionInstance *function = function_from_jsdisp(jsthis);
494 jsstr_t *str;
495 HRESULT hres;
497 TRACE("\n");
499 hres = function->vtbl->toString(function, &str);
500 if(FAILED(hres))
501 return hres;
503 *r = jsval_string(str);
504 return S_OK;
507 static HRESULT Function_get_arguments(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
509 FunctionInstance *function = function_from_jsdisp(jsthis);
510 call_frame_t *frame;
511 HRESULT hres;
513 TRACE("\n");
515 for(frame = ctx->call_ctx; frame; frame = frame->prev_frame) {
516 if(frame->function_instance == &function->dispex) {
517 if(!frame->arguments_obj) {
518 hres = setup_arguments_object(ctx, frame);
519 if(FAILED(hres))
520 return hres;
522 *r = jsval_obj(jsdisp_addref(frame->arguments_obj));
523 return S_OK;
527 *r = jsval_null();
528 return S_OK;
531 function_code_t *Function_get_code(jsdisp_t *jsthis)
533 FunctionInstance *function;
535 assert(is_class(jsthis, JSCLASS_FUNCTION));
536 function = function_from_jsdisp(jsthis);
538 return function->vtbl->get_code(function);
541 static void Function_destructor(jsdisp_t *dispex)
543 FunctionInstance *function = function_from_jsdisp(dispex);
544 function->vtbl->destructor(function);
545 heap_free(function);
548 static const builtin_prop_t Function_props[] = {
549 {applyW, Function_apply, PROPF_METHOD|2},
550 {argumentsW, NULL, 0, Function_get_arguments},
551 {bindW, Function_bind, PROPF_METHOD|PROPF_ES5|1},
552 {callW, Function_call, PROPF_METHOD|1},
553 {lengthW, NULL, 0, Function_get_length},
554 {toStringW, Function_toString, PROPF_METHOD}
557 static const builtin_info_t Function_info = {
558 JSCLASS_FUNCTION,
559 DEFAULT_FUNCTION_VALUE,
560 ARRAY_SIZE(Function_props),
561 Function_props,
562 Function_destructor,
563 NULL
566 static const builtin_prop_t FunctionInst_props[] = {
567 {argumentsW, NULL, 0, Function_get_arguments},
568 {lengthW, NULL, 0, Function_get_length}
571 static const builtin_info_t FunctionInst_info = {
572 JSCLASS_FUNCTION,
573 DEFAULT_FUNCTION_VALUE,
574 ARRAY_SIZE(FunctionInst_props),
575 FunctionInst_props,
576 Function_destructor,
577 NULL
580 static HRESULT create_function(script_ctx_t *ctx, const builtin_info_t *builtin_info, const function_vtbl_t *vtbl, size_t size,
581 DWORD flags, BOOL funcprot, jsdisp_t *prototype, void **ret)
583 FunctionInstance *function;
584 HRESULT hres;
586 function = heap_alloc_zero(size);
587 if(!function)
588 return E_OUTOFMEMORY;
590 if(funcprot)
591 hres = init_dispex(&function->dispex, ctx, builtin_info, prototype);
592 else if(builtin_info)
593 hres = init_dispex_from_constr(&function->dispex, ctx, builtin_info, ctx->function_constr);
594 else
595 hres = init_dispex_from_constr(&function->dispex, ctx, &FunctionInst_info, ctx->function_constr);
596 if(FAILED(hres)) {
597 heap_free(function);
598 return hres;
601 function->vtbl = vtbl;
602 function->flags = flags;
603 function->length = flags & PROPF_ARGMASK;
605 *ret = function;
606 return S_OK;
609 static HRESULT NativeFunction_call(script_ctx_t *ctx, FunctionInstance *func, IDispatch *this_disp, unsigned flags,
610 unsigned argc, jsval_t *argv, jsval_t *r)
612 NativeFunction *function = (NativeFunction*)func;
613 vdisp_t vthis;
614 HRESULT hres;
616 if(this_disp)
617 set_disp(&vthis, this_disp);
618 else
619 set_disp(&vthis, lookup_global_host(ctx));
621 hres = function->proc(ctx, &vthis, flags & ~DISPATCH_JSCRIPT_INTERNAL_MASK, argc, argv, r);
623 vdisp_release(&vthis);
624 return hres;
627 static HRESULT NativeFunction_toString(FunctionInstance *func, jsstr_t **ret)
629 NativeFunction *function = (NativeFunction*)func;
630 DWORD name_len;
631 jsstr_t *str;
632 WCHAR *ptr;
634 static const WCHAR native_prefixW[] = {'\n','f','u','n','c','t','i','o','n',' '};
635 static const WCHAR native_suffixW[] =
636 {'(',')',' ','{','\n',' ',' ',' ',' ','[','n','a','t','i','v','e',' ','c','o','d','e',']','\n','}','\n'};
638 name_len = function->name ? lstrlenW(function->name) : 0;
639 str = jsstr_alloc_buf(ARRAY_SIZE(native_prefixW) + ARRAY_SIZE(native_suffixW) + name_len, &ptr);
640 if(!str)
641 return E_OUTOFMEMORY;
643 memcpy(ptr, native_prefixW, sizeof(native_prefixW));
644 ptr += ARRAY_SIZE(native_prefixW);
645 memcpy(ptr, function->name, name_len*sizeof(WCHAR));
646 ptr += name_len;
647 memcpy(ptr, native_suffixW, sizeof(native_suffixW));
649 *ret = str;
650 return S_OK;
653 static function_code_t *NativeFunction_get_code(FunctionInstance *function)
655 return NULL;
658 static void NativeFunction_destructor(FunctionInstance *function)
662 static const function_vtbl_t NativeFunctionVtbl = {
663 NativeFunction_call,
664 NativeFunction_toString,
665 NativeFunction_get_code,
666 NativeFunction_destructor
669 HRESULT create_builtin_function(script_ctx_t *ctx, builtin_invoke_t value_proc, const WCHAR *name,
670 const builtin_info_t *builtin_info, DWORD flags, jsdisp_t *prototype, jsdisp_t **ret)
672 NativeFunction *function;
673 HRESULT hres;
675 hres = create_function(ctx, builtin_info, &NativeFunctionVtbl, sizeof(NativeFunction), flags, FALSE, NULL, (void**)&function);
676 if(FAILED(hres))
677 return hres;
679 if(builtin_info)
680 hres = jsdisp_define_data_property(&function->function.dispex, lengthW, 0,
681 jsval_number(function->function.length));
682 if(SUCCEEDED(hres))
683 hres = jsdisp_define_data_property(&function->function.dispex, prototypeW, 0, jsval_obj(prototype));
684 if(FAILED(hres)) {
685 jsdisp_release(&function->function.dispex);
686 return hres;
689 function->proc = value_proc;
690 function->name = name;
692 *ret = &function->function.dispex;
693 return S_OK;
696 static HRESULT set_constructor_prop(script_ctx_t *ctx, jsdisp_t *constr, jsdisp_t *prot)
698 static const WCHAR constructorW[] = {'c','o','n','s','t','r','u','c','t','o','r',0};
700 return jsdisp_define_data_property(prot, constructorW, PROPF_WRITABLE | PROPF_CONFIGURABLE,
701 jsval_obj(constr));
704 HRESULT create_builtin_constructor(script_ctx_t *ctx, builtin_invoke_t value_proc, const WCHAR *name,
705 const builtin_info_t *builtin_info, DWORD flags, jsdisp_t *prototype, jsdisp_t **ret)
707 jsdisp_t *constr;
708 HRESULT hres;
710 hres = create_builtin_function(ctx, value_proc, name, builtin_info, flags, prototype, &constr);
711 if(FAILED(hres))
712 return hres;
714 hres = set_constructor_prop(ctx, constr, prototype);
715 if(FAILED(hres)) {
716 jsdisp_release(constr);
717 return hres;
720 *ret = constr;
721 return S_OK;
724 static HRESULT InterpretedFunction_call(script_ctx_t *ctx, FunctionInstance *func, IDispatch *this_obj, unsigned flags,
725 unsigned argc, jsval_t *argv, jsval_t *r)
727 InterpretedFunction *function = (InterpretedFunction*)func;
728 jsdisp_t *new_obj = NULL;
729 DWORD exec_flags = 0;
730 HRESULT hres;
732 TRACE("%p\n", function);
734 if(ctx->state == SCRIPTSTATE_UNINITIALIZED || ctx->state == SCRIPTSTATE_CLOSED) {
735 WARN("Script engine state does not allow running code.\n");
736 return E_UNEXPECTED;
739 if(flags & DISPATCH_CONSTRUCT) {
740 hres = create_object(ctx, &function->function.dispex, &new_obj);
741 if(FAILED(hres))
742 return hres;
743 this_obj = to_disp(new_obj);
746 if(flags & DISPATCH_JSCRIPT_CALLEREXECSSOURCE)
747 exec_flags |= EXEC_RETURN_TO_INTERP;
748 if(flags & DISPATCH_CONSTRUCT)
749 exec_flags |= EXEC_CONSTRUCTOR;
750 hres = exec_source(ctx, exec_flags, function->code, function->func_code, function->scope_chain, this_obj,
751 &function->function.dispex, argc, argv, r);
752 if(new_obj)
753 jsdisp_release(new_obj);
754 return hres;
757 static HRESULT InterpretedFunction_toString(FunctionInstance *func, jsstr_t **ret)
759 InterpretedFunction *function = (InterpretedFunction*)func;
761 *ret = jsstr_alloc_len(function->func_code->source, function->func_code->source_len);
762 return *ret ? S_OK : E_OUTOFMEMORY;
765 static function_code_t *InterpretedFunction_get_code(FunctionInstance *func)
767 InterpretedFunction *function = (InterpretedFunction*)func;
769 return function->func_code;
772 static void InterpretedFunction_destructor(FunctionInstance *func)
774 InterpretedFunction *function = (InterpretedFunction*)func;
776 release_bytecode(function->code);
777 if(function->scope_chain)
778 scope_release(function->scope_chain);
781 static const function_vtbl_t InterpretedFunctionVtbl = {
782 InterpretedFunction_call,
783 InterpretedFunction_toString,
784 InterpretedFunction_get_code,
785 InterpretedFunction_destructor
788 HRESULT create_source_function(script_ctx_t *ctx, bytecode_t *code, function_code_t *func_code,
789 scope_chain_t *scope_chain, jsdisp_t **ret)
791 InterpretedFunction *function;
792 jsdisp_t *prototype;
793 HRESULT hres;
795 hres = create_object(ctx, NULL, &prototype);
796 if(FAILED(hres))
797 return hres;
799 hres = create_function(ctx, NULL, &InterpretedFunctionVtbl, sizeof(InterpretedFunction), PROPF_CONSTR,
800 FALSE, NULL, (void**)&function);
801 if(SUCCEEDED(hres)) {
802 hres = jsdisp_define_data_property(&function->function.dispex, prototypeW, PROPF_WRITABLE,
803 jsval_obj(prototype));
804 if(SUCCEEDED(hres))
805 hres = set_constructor_prop(ctx, &function->function.dispex, prototype);
806 if(FAILED(hres))
807 jsdisp_release(&function->function.dispex);
809 jsdisp_release(prototype);
810 if(FAILED(hres))
811 return hres;
813 if(scope_chain) {
814 scope_addref(scope_chain);
815 function->scope_chain = scope_chain;
818 bytecode_addref(code);
819 function->code = code;
820 function->func_code = func_code;
821 function->function.length = function->func_code->param_cnt;
823 *ret = &function->function.dispex;
824 return S_OK;
827 static HRESULT BindFunction_call(script_ctx_t *ctx, FunctionInstance *func, IDispatch *this_obj, unsigned flags,
828 unsigned argc, jsval_t *argv, jsval_t *r)
830 BindFunction *function = (BindFunction*)func;
831 jsval_t *call_args = NULL;
832 unsigned call_argc;
833 HRESULT hres;
835 TRACE("%p\n", function);
837 call_argc = function->argc + argc;
838 if(call_argc) {
839 call_args = heap_alloc(function->argc * sizeof(*function->args));
840 if(!call_args)
841 return E_OUTOFMEMORY;
843 if(function->argc)
844 memcpy(call_args, function->args, function->argc * sizeof(*call_args));
845 if(argc)
846 memcpy(call_args + function->argc, argv, argc * sizeof(*call_args));
849 hres = function->target->vtbl->call(ctx, function->target, function->this, flags, call_argc, call_args, r);
851 heap_free(call_args);
852 return hres;
855 static HRESULT BindFunction_toString(FunctionInstance *function, jsstr_t **ret)
857 static const WCHAR native_functionW[] =
858 {'\n','f','u','n','c','t','i','o','n','(',')',' ','{','\n',
859 ' ',' ',' ',' ','[','n','a','t','i','v','e',' ','c','o','d','e',']','\n',
860 '}','\n',0};
862 *ret = jsstr_alloc(native_functionW);
863 return *ret ? S_OK : E_OUTOFMEMORY;
866 static function_code_t *BindFunction_get_code(FunctionInstance *function)
868 return NULL;
871 static void BindFunction_destructor(FunctionInstance *func)
873 BindFunction *function = (BindFunction*)func;
874 unsigned i;
876 TRACE("%p\n", function);
878 for(i = 0; i < function->argc; i++)
879 jsval_release(function->args[i]);
880 jsdisp_release(&function->target->dispex);
881 if(function->this)
882 IDispatch_Release(function->this);
885 static const function_vtbl_t BindFunctionVtbl = {
886 BindFunction_call,
887 BindFunction_toString,
888 BindFunction_get_code,
889 BindFunction_destructor
892 static HRESULT create_bind_function(script_ctx_t *ctx, FunctionInstance *target, IDispatch *bound_this, unsigned argc,
893 jsval_t *argv, jsdisp_t **ret)
895 BindFunction *function;
896 HRESULT hres;
898 hres = create_function(ctx, NULL, &BindFunctionVtbl, FIELD_OFFSET(BindFunction, args[argc]), PROPF_METHOD,
899 FALSE, NULL, (void**)&function);
900 if(FAILED(hres))
901 return hres;
903 jsdisp_addref(&target->dispex);
904 function->target = target;
906 if(bound_this)
907 IDispatch_AddRef(function->this = bound_this);
909 for(function->argc = 0; function->argc < argc; function->argc++) {
910 hres = jsval_copy(argv[function->argc], function->args + function->argc);
911 if(FAILED(hres)) {
912 jsdisp_release(&function->function.dispex);
913 return hres;
917 function->function.length = target->length > argc ? target->length - argc : 0;
919 *ret = &function->function.dispex;
920 return S_OK;
923 static HRESULT construct_function(script_ctx_t *ctx, unsigned argc, jsval_t *argv, IDispatch **ret)
925 WCHAR *str = NULL, *ptr;
926 unsigned len = 0, i = 0;
927 bytecode_t *code;
928 jsdisp_t *function;
929 jsstr_t **params = NULL;
930 int j = 0;
931 HRESULT hres = S_OK;
933 static const WCHAR function_anonymousW[] = {'f','u','n','c','t','i','o','n',' ','a','n','o','n','y','m','o','u','s','('};
934 static const WCHAR function_beginW[] = {')',' ','{','\n'};
935 static const WCHAR function_endW[] = {'\n','}',0};
937 if(argc) {
938 params = heap_alloc(argc*sizeof(*params));
939 if(!params)
940 return E_OUTOFMEMORY;
942 if(argc > 2)
943 len = (argc-2)*2; /* separating commas */
944 for(i=0; i < argc; i++) {
945 hres = to_string(ctx, argv[i], params+i);
946 if(FAILED(hres))
947 break;
948 len += jsstr_length(params[i]);
952 if(SUCCEEDED(hres)) {
953 len += ARRAY_SIZE(function_anonymousW) + ARRAY_SIZE(function_beginW) + ARRAY_SIZE(function_endW);
954 str = heap_alloc(len*sizeof(WCHAR));
955 if(str) {
956 memcpy(str, function_anonymousW, sizeof(function_anonymousW));
957 ptr = str + ARRAY_SIZE(function_anonymousW);
958 if(argc > 1) {
959 while(1) {
960 ptr += jsstr_flush(params[j], ptr);
961 if(++j == argc-1)
962 break;
963 *ptr++ = ',';
964 *ptr++ = ' ';
967 memcpy(ptr, function_beginW, sizeof(function_beginW));
968 ptr += ARRAY_SIZE(function_beginW);
969 if(argc)
970 ptr += jsstr_flush(params[argc-1], ptr);
971 memcpy(ptr, function_endW, sizeof(function_endW));
973 TRACE("%s\n", debugstr_w(str));
974 }else {
975 hres = E_OUTOFMEMORY;
979 while(i)
980 jsstr_release(params[--i]);
981 heap_free(params);
982 if(FAILED(hres))
983 return hres;
985 hres = compile_script(ctx, str, 0, 0, NULL, NULL, FALSE, FALSE,
986 ctx->call_ctx ? ctx->call_ctx->bytecode->named_item : NULL, &code);
987 heap_free(str);
988 if(FAILED(hres))
989 return hres;
991 if(code->global_code.func_cnt != 1 || code->global_code.var_cnt != 1) {
992 ERR("Invalid parser result!\n");
993 release_bytecode(code);
994 return E_UNEXPECTED;
997 hres = create_source_function(ctx, code, code->global_code.funcs, NULL, &function);
998 release_bytecode(code);
999 if(FAILED(hres))
1000 return hres;
1002 *ret = to_disp(function);
1003 return S_OK;
1006 static HRESULT FunctionConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1007 jsval_t *r)
1009 HRESULT hres;
1011 TRACE("\n");
1013 switch(flags) {
1014 case DISPATCH_METHOD:
1015 case DISPATCH_CONSTRUCT: {
1016 IDispatch *ret;
1018 hres = construct_function(ctx, argc, argv, &ret);
1019 if(FAILED(hres))
1020 return hres;
1022 *r = jsval_disp(ret);
1023 break;
1025 default:
1026 FIXME("unimplemented flags %x\n", flags);
1027 return E_NOTIMPL;
1030 return S_OK;
1033 static HRESULT FunctionProt_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1034 jsval_t *r)
1036 FIXME("\n");
1037 return E_NOTIMPL;
1040 HRESULT init_function_constr(script_ctx_t *ctx, jsdisp_t *object_prototype)
1042 NativeFunction *prot, *constr;
1043 HRESULT hres;
1045 static const WCHAR FunctionW[] = {'F','u','n','c','t','i','o','n',0};
1047 hres = create_function(ctx, &Function_info, &NativeFunctionVtbl, sizeof(NativeFunction), PROPF_CONSTR,
1048 TRUE, object_prototype, (void**)&prot);
1049 if(FAILED(hres))
1050 return hres;
1052 prot->proc = FunctionProt_value;
1053 prot->name = prototypeW;
1055 hres = create_function(ctx, &FunctionInst_info, &NativeFunctionVtbl, sizeof(NativeFunction), PROPF_CONSTR|1,
1056 TRUE, &prot->function.dispex, (void**)&constr);
1057 if(SUCCEEDED(hres)) {
1058 constr->proc = FunctionConstr_value;
1059 constr->name = FunctionW;
1060 hres = jsdisp_define_data_property(&constr->function.dispex, prototypeW, 0, jsval_obj(&prot->function.dispex));
1061 if(SUCCEEDED(hres))
1062 hres = set_constructor_prop(ctx, &constr->function.dispex, &prot->function.dispex);
1063 if(FAILED(hres))
1064 jsdisp_release(&constr->function.dispex);
1066 jsdisp_release(&prot->function.dispex);
1067 if(FAILED(hres))
1068 return hres;
1070 ctx->function_constr = &constr->function.dispex;
1071 return S_OK;