function.c 23.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * Copyright 2008 Jacek Caban for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "jscript.h"
20
#include "engine.h"
21 22 23 24 25 26

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(jscript);

typedef struct {
27
    jsdisp_t dispex;
28
    builtin_invoke_t value_proc;
29
    const WCHAR *name;
30
    DWORD flags;
31 32 33 34
    source_elements_t *source;
    parameter_t *parameters;
    scope_chain_t *scope_chain;
    parser_ctx_t *parser;
35 36
    const WCHAR *src_str;
    DWORD src_len;
37
    DWORD length;
38
    jsdisp_t *arguments;
39 40
} FunctionInstance;

41 42 43 44 45 46 47 48 49 50
static inline FunctionInstance *function_from_vdisp(vdisp_t *vdisp)
{
    return (FunctionInstance*)vdisp->u.jsdisp;
}

static inline FunctionInstance *function_this(vdisp_t *jsthis)
{
    return is_vclass(jsthis, JSCLASS_FUNCTION) ? function_from_vdisp(jsthis) : NULL;
}

51 52
static const WCHAR prototypeW[] = {'p','r','o','t','o','t', 'y', 'p','e',0};

53 54 55 56
static const WCHAR lengthW[] = {'l','e','n','g','t','h',0};
static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
static const WCHAR applyW[] = {'a','p','p','l','y',0};
static const WCHAR callW[] = {'c','a','l','l',0};
57
static const WCHAR argumentsW[] = {'a','r','g','u','m','e','n','t','s',0};
58

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
static IDispatch *get_this(DISPPARAMS *dp)
{
    DWORD i;

    for(i=0; i < dp->cNamedArgs; i++) {
        if(dp->rgdispidNamedArgs[i] == DISPID_THIS) {
            if(V_VT(dp->rgvarg+i) == VT_DISPATCH)
                return V_DISPATCH(dp->rgvarg+i);

            WARN("This is not VT_DISPATCH\n");
            return NULL;
        }
    }

    TRACE("no this passed\n");
    return NULL;
}

77
static HRESULT init_parameters(jsdisp_t *var_disp, FunctionInstance *function, DISPPARAMS *dp,
78 79 80 81 82 83 84 85
        jsexcept_t *ei, IServiceProvider *caller)
{
    parameter_t *param;
    VARIANT var_empty;
    DWORD cargs, i=0;
    HRESULT hres;

    V_VT(&var_empty) = VT_EMPTY;
Jacek Caban's avatar
Jacek Caban committed
86
    cargs = arg_cnt(dp);
87 88

    for(param = function->parameters; param; param = param->next) {
89
        hres = jsdisp_propput_name(var_disp, param->identifier,
Jacek Caban's avatar
Jacek Caban committed
90
                i < cargs ? get_arg(dp,i) : &var_empty, ei, caller);
91 92 93 94 95 96 97 98 99
        if(FAILED(hres))
            return hres;

        i++;
    }

    return S_OK;
}

100
static HRESULT Arguments_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
101 102 103 104 105 106 107 108 109 110 111 112 113 114
        VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
{
    FIXME("\n");
    return E_NOTIMPL;
}

static const builtin_info_t Arguments_info = {
    JSCLASS_ARGUMENTS,
    {NULL, Arguments_value, 0},
    0, NULL,
    NULL,
    NULL
};

115
static HRESULT create_arguments(script_ctx_t *ctx, IDispatch *calee, DISPPARAMS *dp,
116
        jsexcept_t *ei, IServiceProvider *caller, jsdisp_t **ret)
117
{
118
    jsdisp_t *args;
119 120 121 122
    VARIANT var;
    DWORD i;
    HRESULT hres;

123 124
    static const WCHAR caleeW[] = {'c','a','l','l','e','e',0};

125
    args = heap_alloc_zero(sizeof(jsdisp_t));
126 127 128 129 130 131 132 133 134 135
    if(!args)
        return E_OUTOFMEMORY;

    hres = init_dispex_from_constr(args, ctx, &Arguments_info, ctx->object_constr);
    if(FAILED(hres)) {
        heap_free(args);
        return hres;
    }

    for(i=0; i < arg_cnt(dp); i++) {
136
        hres = jsdisp_propput_idx(args, i, get_arg(dp,i), ei, caller);
137
        if(FAILED(hres))
138 139 140 141 142 143
            break;
    }

    if(SUCCEEDED(hres)) {
        V_VT(&var) = VT_I4;
        V_I4(&var) = arg_cnt(dp);
144
        hres = jsdisp_propput_name(args, lengthW, &var, ei, caller);
145 146 147 148

        if(SUCCEEDED(hres)) {
            V_VT(&var) = VT_DISPATCH;
            V_DISPATCH(&var) = calee;
149
            hres = jsdisp_propput_name(args, caleeW, &var, ei, caller);
150
        }
151 152
    }

153 154 155 156 157 158 159
    if(FAILED(hres)) {
        jsdisp_release(args);
        return hres;
    }

    *ret = args;
    return S_OK;
160 161
}

162 163
static HRESULT create_var_disp(script_ctx_t *ctx, FunctionInstance *function, jsdisp_t *arg_disp,
        DISPPARAMS *dp, jsexcept_t *ei, IServiceProvider *caller, jsdisp_t **ret)
164
{
165
    jsdisp_t *var_disp;
166
    VARIANT var;
167 168
    HRESULT hres;

169
    hres = create_dispex(ctx, NULL, NULL, &var_disp);
170 171 172
    if(FAILED(hres))
        return hres;

173
    var_set_jsdisp(&var, arg_disp);
174
    hres = jsdisp_propput_name(var_disp, argumentsW, &var, ei, caller);
175
    if(SUCCEEDED(hres))
176
        hres = init_parameters(var_disp, function, dp, ei, caller);
177 178 179 180 181
    if(FAILED(hres)) {
        jsdisp_release(var_disp);
        return hres;
    }

182 183 184 185
    *ret = var_disp;
    return S_OK;
}

186
static HRESULT invoke_source(script_ctx_t *ctx, FunctionInstance *function, IDispatch *this_obj, DISPPARAMS *dp,
187 188
        VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
{
189
    jsdisp_t *var_disp, *arg_disp;
190 191 192 193 194 195 196 197 198
    exec_ctx_t *exec_ctx;
    scope_chain_t *scope;
    HRESULT hres;

    if(!function->source) {
        FIXME("no source\n");
        return E_FAIL;
    }

199
    hres = create_arguments(ctx, to_disp(&function->dispex),
200
            dp, ei, caller, &arg_disp);
201 202 203
    if(FAILED(hres))
        return hres;

204 205 206 207 208 209
    hres = create_var_disp(ctx, function, arg_disp, dp, ei, caller, &var_disp);
    if(FAILED(hres)) {
        jsdisp_release(arg_disp);
        return hres;
    }

210 211
    hres = scope_push(function->scope_chain, var_disp, &scope);
    if(SUCCEEDED(hres)) {
212
        hres = create_exec_ctx(ctx, this_obj, var_disp, scope, FALSE, &exec_ctx);
213 214
        scope_release(scope);
    }
215
    jsdisp_release(var_disp);
216
    if(SUCCEEDED(hres)) {
217
        jsdisp_t *prev_args;
218

219 220
        prev_args = function->arguments;
        function->arguments = arg_disp;
221
        hres = exec_source(exec_ctx, function->parser, function->source, FALSE, ei, retv);
222 223 224 225 226
        function->arguments = prev_args;

        jsdisp_release(arg_disp);
        exec_release(exec_ctx);
    }
227 228 229 230

    return hres;
}

231
static HRESULT invoke_constructor(script_ctx_t *ctx, FunctionInstance *function, DISPPARAMS *dp,
232 233
        VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
{
234
    jsdisp_t *this_obj;
235
    VARIANT var;
236 237
    HRESULT hres;

238
    hres = create_object(ctx, &function->dispex, &this_obj);
239 240 241
    if(FAILED(hres))
        return hres;

242
    hres = invoke_source(ctx, function, to_disp(this_obj), dp, &var, ei, caller);
243 244
    if(FAILED(hres)) {
        jsdisp_release(this_obj);
245
        return hres;
246
    }
247

248 249
    if(V_VT(&var) == VT_DISPATCH) {
        jsdisp_release(this_obj);
250
        V_VT(retv) = VT_DISPATCH;
251 252 253
        V_DISPATCH(retv) = V_DISPATCH(&var);
    }else {
        VariantClear(&var);
254
        var_set_jsdisp(retv, this_obj);
255
    }
256 257 258
    return S_OK;
}

259
static HRESULT invoke_value_proc(script_ctx_t *ctx, FunctionInstance *function, IDispatch *this_disp, WORD flags, DISPPARAMS *dp,
260 261
        VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
{
262
    vdisp_t vthis;
263 264 265
    HRESULT hres;

    if(this_disp)
266
        set_disp(&vthis, this_disp);
267 268
    else if(ctx->host_global)
        set_disp(&vthis, ctx->host_global);
269
    else
270
        set_jsdisp(&vthis, ctx->global);
271

272
    hres = function->value_proc(ctx, &vthis, flags, dp, retv, ei, caller);
273

274
    vdisp_release(&vthis);
275 276 277
    return hres;
}

278
static HRESULT call_function(script_ctx_t *ctx, FunctionInstance *function, IDispatch *this_obj, DISPPARAMS *args,
279 280
        VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
{
281 282
    if(function->value_proc)
        return invoke_value_proc(ctx, function, this_obj, DISPATCH_METHOD, args, retv, ei, caller);
283

284
    return invoke_source(ctx, function, this_obj, args, retv, ei, caller);
285 286
}

287 288 289 290
static HRESULT function_to_string(FunctionInstance *function, BSTR *ret)
{
    BSTR str;

291 292 293 294
    static const WCHAR native_prefixW[] = {'\n','f','u','n','c','t','i','o','n',' '};
    static const WCHAR native_suffixW[] =
        {'(',')',' ','{','\n',' ',' ',' ',' ','[','n','a','t','i','v','e',' ','c','o','d','e',']','\n','}','\n'};

295
    if(function->value_proc) {
296
        DWORD name_len;
297

298 299 300 301 302 303 304 305 306 307 308 309 310
        name_len = strlenW(function->name);
        str = SysAllocStringLen(NULL, sizeof(native_prefixW) + name_len*sizeof(WCHAR) + sizeof(native_suffixW));
        if(!str)
            return E_OUTOFMEMORY;

        memcpy(str, native_prefixW, sizeof(native_prefixW));
        memcpy(str + sizeof(native_prefixW)/sizeof(WCHAR), function->name, name_len*sizeof(WCHAR));
        memcpy(str + sizeof(native_prefixW)/sizeof(WCHAR) + name_len, native_suffixW, sizeof(native_suffixW));
    }else {
        str = SysAllocStringLen(function->src_str, function->src_len);
        if(!str)
            return E_OUTOFMEMORY;
    }
311 312 313 314 315

    *ret = str;
    return S_OK;
}

316
static HRESULT Function_length(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
317 318
        VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
{
319
    FunctionInstance *This = function_from_vdisp(jsthis);
320 321 322 323 324 325 326 327 328 329 330 331 332 333

    TRACE("%p %d\n", This, This->length);

    switch(flags) {
    case DISPATCH_PROPERTYGET:
        V_VT(retv) = VT_I4;
        V_I4(retv) = This->length;
        break;
    default:
        FIXME("unimplemented flags %x\n", flags);
        return E_NOTIMPL;
    }

    return S_OK;
334 335
}

336
static HRESULT Function_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
337 338
        VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
{
339 340 341 342 343 344
    FunctionInstance *function;
    BSTR str;
    HRESULT hres;

    TRACE("\n");

345
    if(!(function = function_this(jsthis)))
346
        return throw_type_error(ctx, ei, JS_E_FUNCTION_EXPECTED, NULL);
347 348 349 350 351 352 353 354 355 356 357 358

    hres = function_to_string(function, &str);
    if(FAILED(hres))
        return hres;

    if(retv) {
        V_VT(retv) = VT_BSTR;
        V_BSTR(retv) = str;
    }else {
        SysFreeString(str);
    }
    return S_OK;
359 360
}

361
static HRESULT array_to_args(script_ctx_t *ctx, jsdisp_t *arg_array, jsexcept_t *ei, IServiceProvider *caller,
362 363 364 365 366 367
        DISPPARAMS *args)
{
    VARIANT var, *argv;
    DWORD length, i;
    HRESULT hres;

368
    hres = jsdisp_propget_name(arg_array, lengthW, &var, ei, NULL/*FIXME*/);
369 370 371
    if(FAILED(hres))
        return hres;

372
    hres = to_uint32(ctx, &var, ei, &length);
373 374 375 376 377
    VariantClear(&var);
    if(FAILED(hres))
        return hres;

    argv = heap_alloc(length * sizeof(VARIANT));
378
    if(!argv)
379 380 381
        return E_OUTOFMEMORY;

    for(i=0; i<length; i++) {
382 383 384 385
        hres = jsdisp_get_idx(arg_array, i, argv+i, ei, caller);
        if(hres == DISP_E_UNKNOWNNAME)
            V_VT(argv+i) = VT_EMPTY;
        else if(FAILED(hres)) {
386 387 388 389 390 391 392 393 394 395 396 397
            while(i--)
                VariantClear(argv+i);
            heap_free(argv);
            return hres;
        }
    }

    args->cArgs = length;
    args->rgvarg = argv;
    return S_OK;
}

398
static HRESULT Function_apply(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
399
        VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
400
{
401 402 403
    FunctionInstance *function;
    DISPPARAMS args = {NULL,NULL,0,0};
    DWORD argc, i;
404
    IDispatch *this_obj = NULL;
405 406 407 408
    HRESULT hres = S_OK;

    TRACE("\n");

409
    if(!(function = function_this(jsthis)))
410
        return throw_type_error(ctx, ei, JS_E_FUNCTION_EXPECTED, NULL);
411 412 413

    argc = arg_cnt(dp);
    if(argc) {
414 415 416 417 418 419 420
        VARIANT *v = get_arg(dp,0);

        if(V_VT(v) != VT_EMPTY && V_VT(v) != VT_NULL) {
            hres = to_object(ctx, v, &this_obj);
            if(FAILED(hres))
                return hres;
        }
421 422 423
    }

    if(argc >= 2) {
424
        jsdisp_t *arg_array = NULL;
425 426 427

        if(V_VT(get_arg(dp,1)) == VT_DISPATCH) {
            arg_array = iface_to_jsdisp((IUnknown*)V_DISPATCH(get_arg(dp,1)));
428 429
            if(arg_array &&
               (!is_class(arg_array, JSCLASS_ARRAY) && !is_class(arg_array, JSCLASS_ARGUMENTS) )) {
430 431 432 433 434 435
                jsdisp_release(arg_array);
                arg_array = NULL;
            }
        }

        if(arg_array) {
436
            hres = array_to_args(ctx, arg_array, ei, caller, &args);
437 438
            jsdisp_release(arg_array);
        }else {
439
            FIXME("throw TypeError\n");
440 441 442 443
            hres = E_FAIL;
        }
    }

444 445
    if(SUCCEEDED(hres))
       hres = call_function(ctx, function, this_obj, &args, retv, ei, caller);
446 447 448 449 450 451 452

    if(this_obj)
        IDispatch_Release(this_obj);
    for(i=0; i<args.cArgs; i++)
        VariantClear(args.rgvarg+i);
    heap_free(args.rgvarg);
    return hres;
453 454
}

455
static HRESULT Function_call(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
456
        VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
457
{
458 459 460 461 462 463 464 465
    FunctionInstance *function;
    DISPPARAMS args = {NULL,NULL,0,0};
    IDispatch *this_obj = NULL;
    DWORD argc;
    HRESULT hres;

    TRACE("\n");

466
    if(!(function = function_this(jsthis)))
467
        return throw_type_error(ctx, ei, JS_E_FUNCTION_EXPECTED, NULL);
468 469 470

    argc = arg_cnt(dp);
    if(argc) {
471 472 473 474 475 476 477 478
        VARIANT *v = get_arg(dp,0);

        if(V_VT(v) != VT_EMPTY && V_VT(v) != VT_NULL) {
            hres = to_object(ctx, v, &this_obj);
            if(FAILED(hres))
                return hres;
        }

479 480 481 482 483 484
        args.cArgs = argc-1;
    }

    if(args.cArgs)
        args.rgvarg = dp->rgvarg + dp->cArgs - args.cArgs-1;

485
    hres = call_function(ctx, function, this_obj, &args, retv, ei, caller);
486 487 488 489

    if(this_obj)
        IDispatch_Release(this_obj);
    return hres;
490 491
}

492
HRESULT Function_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
493
        VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
494
{
495 496 497 498
    FunctionInstance *function;

    TRACE("\n");

499
    if(!is_vclass(jsthis, JSCLASS_FUNCTION)) {
500 501 502 503
        ERR("dispex is not a function\n");
        return E_FAIL;
    }

504
    function = (FunctionInstance*)jsthis->u.jsdisp;
505 506 507 508

    switch(flags) {
    case DISPATCH_METHOD:
        if(function->value_proc)
509
            return invoke_value_proc(ctx, function, get_this(dp), flags, dp, retv, ei, caller);
510

511
        return invoke_source(ctx, function, get_this(dp), dp, retv, ei, caller);
512

513 514 515 516 517 518 519 520 521 522 523 524 525
    case DISPATCH_PROPERTYGET: {
        HRESULT hres;
        BSTR str;

        hres = function_to_string(function, &str);
        if(FAILED(hres))
            return hres;

        V_VT(retv) = VT_BSTR;
        V_BSTR(retv) = str;
        break;
    }

526 527
    case DISPATCH_CONSTRUCT:
        if(function->value_proc)
528
            return invoke_value_proc(ctx, function, get_this(dp), flags, dp, retv, ei, caller);
529

530
        return invoke_constructor(ctx, function, dp, retv, ei, caller);
531

532 533 534 535 536 537
    default:
        FIXME("not implemented flags %x\n", flags);
        return E_NOTIMPL;
    }

    return S_OK;
538 539
}

540 541 542 543 544 545 546 547 548 549 550
static HRESULT Function_arguments(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
        DISPPARAMS *dp, VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
{
    FunctionInstance *function = (FunctionInstance*)jsthis->u.jsdisp;
    HRESULT hres = S_OK;

    TRACE("\n");

    switch(flags) {
    case DISPATCH_PROPERTYGET: {
        if(function->arguments) {
551
            jsdisp_addref(function->arguments);
552
            var_set_jsdisp(retv, function->arguments);
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
        }else {
            V_VT(retv) = VT_NULL;
        }
        break;
    }
    case DISPATCH_PROPERTYPUT:
        break;
    default:
        FIXME("unimplemented flags %x\n", flags);
        hres = E_NOTIMPL;
    }

    return hres;
}

568
static void Function_destructor(jsdisp_t *dispex)
569 570 571
{
    FunctionInstance *This = (FunctionInstance*)dispex;

572 573 574 575
    if(This->parser)
        parser_release(This->parser);
    if(This->scope_chain)
        scope_release(This->scope_chain);
576 577 578 579
    heap_free(This);
}

static const builtin_prop_t Function_props[] = {
580
    {applyW,                 Function_apply,                 PROPF_METHOD|2},
581
    {argumentsW,             Function_arguments,             0},
582
    {callW,                  Function_call,                  PROPF_METHOD|1},
583
    {lengthW,                Function_length,                0},
584
    {toStringW,              Function_toString,              PROPF_METHOD}
585 586 587 588 589 590 591 592 593 594 595
};

static const builtin_info_t Function_info = {
    JSCLASS_FUNCTION,
    {NULL, Function_value, 0},
    sizeof(Function_props)/sizeof(*Function_props),
    Function_props,
    Function_destructor,
    NULL
};

596
static HRESULT create_function(script_ctx_t *ctx, const builtin_info_t *builtin_info, DWORD flags,
597
        BOOL funcprot, jsdisp_t *prototype, FunctionInstance **ret)
598 599 600 601 602 603 604 605
{
    FunctionInstance *function;
    HRESULT hres;

    function = heap_alloc_zero(sizeof(FunctionInstance));
    if(!function)
        return E_OUTOFMEMORY;

606 607
    if(funcprot)
        hres = init_dispex(&function->dispex, ctx, &Function_info, prototype);
608 609
    else if(builtin_info)
        hres = init_dispex_from_constr(&function->dispex, ctx, builtin_info, ctx->function_constr);
610 611
    else
        hres = init_dispex_from_constr(&function->dispex, ctx, &Function_info, ctx->function_constr);
612 613 614 615 616 617
    if(FAILED(hres))
        return hres;

    function->flags = flags;
    function->length = flags & PROPF_ARGMASK;

618 619 620
    *ret = function;
    return S_OK;
}
621

622
static HRESULT set_prototype(script_ctx_t *ctx, jsdisp_t *dispex, jsdisp_t *prototype)
623 624 625
{
    jsexcept_t jsexcept;
    VARIANT var;
626

627
    var_set_jsdisp(&var, prototype);
628
    memset(&jsexcept, 0, sizeof(jsexcept));
629

630
    return jsdisp_propput_name(dispex, prototypeW, &var, &jsexcept, NULL/*FIXME*/);
631 632
}

633
HRESULT create_builtin_function(script_ctx_t *ctx, builtin_invoke_t value_proc, const WCHAR *name,
634
        const builtin_info_t *builtin_info, DWORD flags, jsdisp_t *prototype, jsdisp_t **ret)
635 636 637 638
{
    FunctionInstance *function;
    HRESULT hres;

639
    hres = create_function(ctx, builtin_info, flags, FALSE, NULL, &function);
640 641 642
    if(FAILED(hres))
        return hres;

643 644 645 646 647 648 649 650 651 652
    if(builtin_info) {
        VARIANT var;

        V_VT(&var) = VT_I4;
        V_I4(&var) = function->length;
        hres = jsdisp_propput_const(&function->dispex, lengthW, &var);
    }

    if(SUCCEEDED(hres))
        hres = set_prototype(ctx, &function->dispex, prototype);
653 654 655 656 657
    if(FAILED(hres)) {
        jsdisp_release(&function->dispex);
        return hres;
    }

658
    function->value_proc = value_proc;
659
    function->name = name;
660 661 662 663

    *ret = &function->dispex;
    return S_OK;
}
664 665

HRESULT create_source_function(parser_ctx_t *ctx, parameter_t *parameters, source_elements_t *source,
666
        scope_chain_t *scope_chain, const WCHAR *src_str, DWORD src_len, jsdisp_t **ret)
667 668
{
    FunctionInstance *function;
669
    jsdisp_t *prototype;
670 671 672 673
    parameter_t *iter;
    DWORD length = 0;
    HRESULT hres;

674 675 676 677
    hres = create_object(ctx->script, NULL, &prototype);
    if(FAILED(hres))
        return hres;

678 679 680 681 682 683
    hres = create_function(ctx->script, NULL, PROPF_CONSTR, FALSE, NULL, &function);
    if(SUCCEEDED(hres)) {
        hres = set_prototype(ctx->script, &function->dispex, prototype);
        if(FAILED(hres))
            jsdisp_release(&function->dispex);
    }
684
    jsdisp_release(prototype);
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
    if(FAILED(hres))
        return hres;

    function->source = source;
    function->parameters = parameters;

    if(scope_chain) {
        scope_addref(scope_chain);
        function->scope_chain = scope_chain;
    }

    parser_addref(ctx);
    function->parser = ctx;

    for(iter = parameters; iter; iter = iter->next)
        length++;
    function->length = length;

703 704 705
    function->src_str = src_str;
    function->src_len = src_len;

706 707 708
    *ret = &function->dispex;
    return S_OK;
}
709

710 711 712 713 714 715
static HRESULT construct_function(script_ctx_t *ctx, DISPPARAMS *dp, jsexcept_t *ei, IDispatch **ret)
{
    function_expression_t *expr;
    WCHAR *str = NULL, *ptr;
    DWORD argc, len = 0, l;
    parser_ctx_t *parser;
716
    jsdisp_t *function;
717
    BSTR *params = NULL;
718
    int i=0, j=0;
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
    HRESULT hres = S_OK;

    static const WCHAR function_anonymousW[] = {'f','u','n','c','t','i','o','n',' ','a','n','o','n','y','m','o','u','s','('};
    static const WCHAR function_beginW[] = {')',' ','{','\n'};
    static const WCHAR function_endW[] = {'\n','}',0};

    argc = arg_cnt(dp);
    if(argc) {
        params = heap_alloc(argc*sizeof(BSTR));
        if(!params)
            return E_OUTOFMEMORY;

        if(argc > 2)
            len = (argc-2)*2; /* separating commas */
        for(i=0; i < argc; i++) {
            hres = to_string(ctx, get_arg(dp,i), ei, params+i);
            if(FAILED(hres))
                break;
            len += SysStringLen(params[i]);
        }
    }

    if(SUCCEEDED(hres)) {
        len += (sizeof(function_anonymousW) + sizeof(function_beginW) + sizeof(function_endW)) / sizeof(WCHAR);
        str = heap_alloc(len*sizeof(WCHAR));
        if(str) {
            memcpy(str, function_anonymousW, sizeof(function_anonymousW));
            ptr = str + sizeof(function_anonymousW)/sizeof(WCHAR);
            if(argc > 1) {
                while(1) {
                    l = SysStringLen(params[j]);
                    memcpy(ptr, params[j], l*sizeof(WCHAR));
                    ptr += l;
                    if(++j == argc-1)
                        break;
                    *ptr++ = ',';
                    *ptr++ = ' ';
                }
            }
            memcpy(ptr, function_beginW, sizeof(function_beginW));
            ptr += sizeof(function_beginW)/sizeof(WCHAR);
            if(argc) {
                l = SysStringLen(params[argc-1]);
                memcpy(ptr, params[argc-1], l*sizeof(WCHAR));
                ptr += l;
            }
            memcpy(ptr, function_endW, sizeof(function_endW));

            TRACE("%s\n", debugstr_w(str));
        }else {
            hres = E_OUTOFMEMORY;
        }
    }

    while(--i >= 0)
        SysFreeString(params[i]);
    heap_free(params);
    if(FAILED(hres))
        return hres;

    hres = script_parse(ctx, str, NULL, &parser);
    heap_free(str);
    if(FAILED(hres))
        return hres;

    if(!parser->source || !parser->source->functions || parser->source->functions->next || parser->source->variables) {
        ERR("Invalid parser result!\n");
        parser_release(parser);
        return E_UNEXPECTED;
    }
    expr = parser->source->functions->expr;

    hres = create_source_function(parser, expr->parameter_list, expr->source_elements, NULL, expr->src_str,
            expr->src_len, &function);
    parser_release(parser);
    if(FAILED(hres))
        return hres;

797
    *ret = to_disp(function);
798 799 800
    return S_OK;
}

801 802 803
static HRESULT FunctionConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
        VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
{
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825
    HRESULT hres;

    TRACE("\n");

    switch(flags) {
    case DISPATCH_CONSTRUCT: {
        IDispatch *ret;

        hres = construct_function(ctx, dp, ei, &ret);
        if(FAILED(hres))
            return hres;

        V_VT(retv) = VT_DISPATCH;
        V_DISPATCH(retv) = ret;
        break;
    }
    default:
        FIXME("unimplemented flags %x\n", flags);
        return E_NOTIMPL;
    }

    return S_OK;
826 827 828 829 830 831 832 833 834
}

static HRESULT FunctionProt_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
        VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
{
    FIXME("\n");
    return E_NOTIMPL;
}

835
HRESULT init_function_constr(script_ctx_t *ctx, jsdisp_t *object_prototype)
836 837 838 839
{
    FunctionInstance *prot, *constr;
    HRESULT hres;

840 841
    static const WCHAR FunctionW[] = {'F','u','n','c','t','i','o','n',0};

842
    hres = create_function(ctx, NULL, PROPF_CONSTR, TRUE, object_prototype, &prot);
843 844 845 846
    if(FAILED(hres))
        return hres;

    prot->value_proc = FunctionProt_value;
847
    prot->name = prototypeW;
848

849
    hres = create_function(ctx, NULL, PROPF_CONSTR|1, TRUE, &prot->dispex, &constr);
850 851
    if(SUCCEEDED(hres)) {
        constr->value_proc = FunctionConstr_value;
852
        constr->name = FunctionW;
853 854 855 856
        hres = set_prototype(ctx, &constr->dispex, &prot->dispex);
        if(FAILED(hres))
            jsdisp_release(&constr->dispex);
    }
857 858 859 860 861
    jsdisp_release(&prot->dispex);
    if(FAILED(hres))
        return hres;

    ctx->function_constr = &constr->dispex;
862
    return S_OK;
863
}