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
function_from_jsdisp(jsdisp_t * jsdisp)74 static inline FunctionInstance *function_from_jsdisp(jsdisp_t *jsdisp)
75 {
76 return CONTAINING_RECORD(jsdisp, FunctionInstance, dispex);
77 }
78
function_from_vdisp(vdisp_t * vdisp)79 static inline FunctionInstance *function_from_vdisp(vdisp_t *vdisp)
80 {
81 return function_from_jsdisp(vdisp->u.jsdisp);
82 }
83
function_this(vdisp_t * jsthis)84 static inline FunctionInstance *function_this(vdisp_t *jsthis)
85 {
86 return is_vclass(jsthis, JSCLASS_FUNCTION) ? function_from_vdisp(jsthis) : NULL;
87 }
88
arguments_from_jsdisp(jsdisp_t * jsdisp)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
Arguments_value(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)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
Arguments_destructor(jsdisp_t * jsdisp)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
Arguments_idx_length(jsdisp_t * jsdisp)127 static unsigned Arguments_idx_length(jsdisp_t *jsdisp)
128 {
129 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
130 return arguments->argc;
131 }
132
get_argument_ref(ArgumentsInstance * arguments,unsigned idx)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
Arguments_idx_get(jsdisp_t * jsdisp,unsigned idx,jsval_t * r)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
Arguments_idx_put(jsdisp_t * jsdisp,unsigned idx,jsval_t val)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, ©);
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
setup_arguments_object(script_ctx_t * ctx,call_frame_t * frame)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
detach_arguments_object(jsdisp_t * args_disp)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
Function_invoke(jsdisp_t * func_this,IDispatch * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)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
Function_get_length(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
Function_toString(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)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
array_to_args(script_ctx_t * ctx,jsdisp_t * arg_array,unsigned * argc,jsval_t ** ret)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
Function_apply(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)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
Function_call(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)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
Function_bind(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)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
Function_value(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)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
Function_get_value(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
Function_get_arguments(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
Function_destructor(jsdisp_t * dispex)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
create_function(script_ctx_t * ctx,const builtin_info_t * builtin_info,const function_vtbl_t * vtbl,size_t size,DWORD flags,BOOL funcprot,jsdisp_t * prototype,void ** ret)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
NativeFunction_call(script_ctx_t * ctx,FunctionInstance * func,IDispatch * this_disp,unsigned flags,unsigned argc,jsval_t * argv,jsval_t * r)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
NativeFunction_toString(FunctionInstance * func,jsstr_t ** ret)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
NativeFunction_destructor(FunctionInstance * function)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
create_builtin_function(script_ctx_t * ctx,builtin_invoke_t value_proc,const WCHAR * name,const builtin_info_t * builtin_info,DWORD flags,jsdisp_t * prototype,jsdisp_t ** ret)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
set_constructor_prop(script_ctx_t * ctx,jsdisp_t * constr,jsdisp_t * prot)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
create_builtin_constructor(script_ctx_t * ctx,builtin_invoke_t value_proc,const WCHAR * name,const builtin_info_t * builtin_info,DWORD flags,jsdisp_t * prototype,jsdisp_t ** ret)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
InterpretedFunction_call(script_ctx_t * ctx,FunctionInstance * func,IDispatch * this_obj,unsigned flags,unsigned argc,jsval_t * argv,jsval_t * r)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
InterpretedFunction_toString(FunctionInstance * func,jsstr_t ** ret)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
InterpretedFunction_destructor(FunctionInstance * func)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
create_source_function(script_ctx_t * ctx,bytecode_t * code,function_code_t * func_code,scope_chain_t * scope_chain,jsdisp_t ** ret)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
BindFunction_call(script_ctx_t * ctx,FunctionInstance * func,IDispatch * this_obj,unsigned flags,unsigned argc,jsval_t * argv,jsval_t * r)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
BindFunction_toString(FunctionInstance * function,jsstr_t ** ret)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
BindFunction_destructor(FunctionInstance * func)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
create_bind_function(script_ctx_t * ctx,FunctionInstance * target,IDispatch * bound_this,unsigned argc,jsval_t * argv,jsdisp_t ** ret)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
construct_function(script_ctx_t * ctx,unsigned argc,jsval_t * argv,IDispatch ** ret)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
FunctionConstr_value(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)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
FunctionProt_value(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)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
init_function_constr(script_ctx_t * ctx,jsdisp_t * object_prototype)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