jsutils.c 24.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * 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
 */

19 20 21
#include "config.h"
#include "wine/port.h"

22
#include <math.h>
23
#include <assert.h>
24

25
#include "jscript.h"
26
#include "engine.h"
27 28 29 30

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(jscript);
31
WINE_DECLARE_DEBUG_CHANNEL(heap);
32

33 34
const char *debugstr_jsval(const jsval_t v)
{
35
    switch(jsval_type(v)) {
36 37 38 39 40 41 42
    case JSV_UNDEFINED:
        return "undefined";
    case JSV_NULL:
        return "null";
    case JSV_OBJECT:
        return wine_dbg_sprintf("obj(%p)", get_object(v));
    case JSV_STRING:
43
        return wine_dbg_sprintf("str(%s)", debugstr_jsstr(get_string(v)));
44 45 46 47 48 49 50 51 52 53 54 55
    case JSV_NUMBER:
        return wine_dbg_sprintf("%lf", get_number(v));
    case JSV_BOOL:
        return get_bool(v) ? "true" : "false";
    case JSV_VARIANT:
        return debugstr_variant(get_variant(v));
    }

    assert(0);
    return NULL;
}

56 57 58 59 60
BOOL is_finite(double n)
{
    return !isnan(n) && !isinf(n);
}

61
#define MIN_BLOCK_SIZE  128
62
#define ARENA_FREE_FILLER  0xaa
63 64 65 66 67 68

static inline DWORD block_size(DWORD block)
{
    return MIN_BLOCK_SIZE << block;
}

69
void heap_pool_init(heap_pool_t *heap)
70 71 72 73 74
{
    memset(heap, 0, sizeof(*heap));
    list_init(&heap->custom_blocks);
}

75
void *heap_pool_alloc(heap_pool_t *heap, DWORD size)
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
{
    struct list *list;
    void *tmp;

    if(!heap->block_cnt) {
        if(!heap->blocks) {
            heap->blocks = heap_alloc(sizeof(void*));
            if(!heap->blocks)
                return NULL;
        }

        tmp = heap_alloc(block_size(0));
        if(!tmp)
            return NULL;

        heap->blocks[0] = tmp;
        heap->block_cnt = 1;
    }

95
    if(heap->offset + size <= block_size(heap->last_block)) {
96 97 98 99 100
        tmp = ((BYTE*)heap->blocks[heap->last_block])+heap->offset;
        heap->offset += size;
        return tmp;
    }

101
    if(size <= block_size(heap->last_block+1)) {
102 103 104 105 106
        if(heap->last_block+1 == heap->block_cnt) {
            tmp = heap_realloc(heap->blocks, (heap->block_cnt+1)*sizeof(void*));
            if(!tmp)
                return NULL;

107 108 109 110
            heap->blocks = tmp;
            heap->blocks[heap->block_cnt] = heap_alloc(block_size(heap->block_cnt));
            if(!heap->blocks[heap->block_cnt])
                return NULL;
111

112 113
            heap->block_cnt++;
        }
114 115 116 117 118 119 120 121 122 123 124 125 126 127

        heap->last_block++;
        heap->offset = size;
        return heap->blocks[heap->last_block];
    }

    list = heap_alloc(size + sizeof(struct list));
    if(!list)
        return NULL;

    list_add_head(&heap->custom_blocks, list);
    return list+1;
}

128
void *heap_pool_grow(heap_pool_t *heap, void *mem, DWORD size, DWORD inc)
129
{
130 131
    void *ret;

132 133 134 135 136 137
    if(mem == (BYTE*)heap->blocks[heap->last_block] + heap->offset-size
       && heap->offset+inc < block_size(heap->last_block)) {
        heap->offset += inc;
        return mem;
    }

138
    ret = heap_pool_alloc(heap, size+inc);
139
    if(ret) /* FIXME: avoid copying for custom blocks */
140 141
        memcpy(ret, mem, size);
    return ret;
142 143
}

144
void heap_pool_clear(heap_pool_t *heap)
145 146 147
{
    struct list *tmp;

148 149 150
    if(!heap)
        return;

151
    while((tmp = list_head(&heap->custom_blocks))) {
152 153 154
        list_remove(tmp);
        heap_free(tmp);
    }
155

156 157 158 159 160 161 162
    if(WARN_ON(heap)) {
        DWORD i;

        for(i=0; i < heap->block_cnt; i++)
            memset(heap->blocks[i], ARENA_FREE_FILLER, block_size(i));
    }

163
    heap->last_block = heap->offset = 0;
164
    heap->mark = FALSE;
165 166
}

167
void heap_pool_free(heap_pool_t *heap)
168 169 170
{
    DWORD i;

171
    heap_pool_clear(heap);
172 173 174 175 176

    for(i=0; i < heap->block_cnt; i++)
        heap_free(heap->blocks[i]);
    heap_free(heap->blocks);

177
    heap_pool_init(heap);
178
}
179

180
heap_pool_t *heap_pool_mark(heap_pool_t *heap)
181 182 183 184 185 186 187 188
{
    if(heap->mark)
        return NULL;

    heap->mark = TRUE;
    return heap;
}

189 190
void jsval_release(jsval_t val)
{
191
    switch(jsval_type(val)) {
192
    case JSV_OBJECT:
193 194
        if(get_object(val))
            IDispatch_Release(get_object(val));
195 196
        break;
    case JSV_STRING:
197
        jsstr_release(get_string(val));
198 199
        break;
    case JSV_VARIANT:
200 201
        VariantClear(get_variant(val));
        heap_free(get_variant(val));
202 203 204 205 206 207
        break;
    default:
        break;
    }
}

208
static HRESULT jsval_variant(jsval_t *val, VARIANT *var)
209
{
210
    VARIANT *v;
211 212
    HRESULT hres;

213 214
    __JSVAL_TYPE(*val) = JSV_VARIANT;
    __JSVAL_VAR(*val) = v = heap_alloc(sizeof(VARIANT));
215 216
    if(!v) {
        *val = jsval_undefined();
217
        return E_OUTOFMEMORY;
218
    }
219

220 221
    V_VT(v) = VT_EMPTY;
    hres = VariantCopy(v, var);
222 223
    if(FAILED(hres)) {
        *val = jsval_undefined();
224
        heap_free(v);
225
    }
226 227 228
    return hres;
}

229 230
HRESULT jsval_copy(jsval_t v, jsval_t *r)
{
231
    switch(jsval_type(v)) {
232 233 234 235 236 237 238
    case JSV_UNDEFINED:
    case JSV_NULL:
    case JSV_NUMBER:
    case JSV_BOOL:
        *r = v;
        return S_OK;
    case JSV_OBJECT:
239 240
        if(get_object(v))
            IDispatch_AddRef(get_object(v));
241 242 243
        *r = v;
        return S_OK;
    case JSV_STRING: {
244 245
        jsstr_addref(get_string(v));
        *r = v;
246 247 248 249 250 251 252 253 254 255
        return S_OK;
    }
    case JSV_VARIANT:
        return jsval_variant(r, get_variant(v));
    }

    assert(0);
    return E_FAIL;
}

256 257
HRESULT variant_to_jsval(VARIANT *var, jsval_t *r)
{
258 259 260
    if(V_VT(var) == (VT_VARIANT|VT_BYREF))
        var = V_VARIANTREF(var);

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
    switch(V_VT(var)) {
    case VT_EMPTY:
        *r = jsval_undefined();
        return S_OK;
    case VT_NULL:
        *r = jsval_null();
        return S_OK;
    case VT_BOOL:
        *r = jsval_bool(V_BOOL(var));
        return S_OK;
    case VT_I4:
        *r = jsval_number(V_I4(var));
        return S_OK;
    case VT_R8:
        *r = jsval_number(V_R8(var));
        return S_OK;
    case VT_BSTR: {
278 279
        jsstr_t *str;

280 281 282 283 284 285 286
        if(V_BSTR(var)) {
            str = jsstr_alloc_len(V_BSTR(var), SysStringLen(V_BSTR(var)));
            if(!str)
                return E_OUTOFMEMORY;
        }else {
            str = jsstr_null_bstr();
        }
287

288 289 290 291
        *r = jsval_string(str);
        return S_OK;
    }
    case VT_DISPATCH: {
292 293
        if(V_DISPATCH(var))
            IDispatch_AddRef(V_DISPATCH(var));
294 295 296 297
        *r = jsval_disp(V_DISPATCH(var));
        return S_OK;
    }
    case VT_I2:
298 299
        *r = jsval_number(V_I2(var));
        return S_OK;
300 301 302
    case VT_UI2:
        *r = jsval_number(V_UI2(var));
        return S_OK;
303
    case VT_INT:
304 305
        *r = jsval_number(V_INT(var));
        return S_OK;
306 307 308
    case VT_UI4:
        *r = jsval_number(V_UI4(var));
        return S_OK;
309 310 311 312 313 314 315
    case VT_UI8:
        /*
         * Native doesn't support VT_UI8 here, but it's needed for IE9+ APIs
         * (native IE9 doesn't use jscript.dll for JavaScript).
         */
        *r = jsval_number(V_UI8(var));
        return S_OK;
316 317 318
    case VT_R4:
        *r = jsval_number(V_R4(var));
        return S_OK;
319 320 321 322 323 324 325 326 327 328
    case VT_UNKNOWN:
        if(V_UNKNOWN(var)) {
            IDispatch *disp;
            HRESULT hres;

            hres = IUnknown_QueryInterface(V_UNKNOWN(var), &IID_IDispatch, (void**)&disp);
            if(SUCCEEDED(hres)) {
                *r = jsval_disp(disp);
                return S_OK;
            }
329 330 331
        }else {
            *r = jsval_disp(NULL);
            return S_OK;
332 333
        }
        /* fall through */
334 335 336 337 338 339 340
    default:
        return jsval_variant(r, var);
    }
}

HRESULT jsval_to_variant(jsval_t val, VARIANT *retv)
{
341
    switch(jsval_type(val)) {
342 343 344 345 346 347 348 349
    case JSV_UNDEFINED:
        V_VT(retv) = VT_EMPTY;
        return S_OK;
    case JSV_NULL:
        V_VT(retv) = VT_NULL;
        return S_OK;
    case JSV_OBJECT:
        V_VT(retv) = VT_DISPATCH;
350 351 352
        if(get_object(val))
            IDispatch_AddRef(get_object(val));
        V_DISPATCH(retv) = get_object(val);
353
        return S_OK;
354 355 356
    case JSV_STRING: {
        jsstr_t *str = get_string(val);

357
        V_VT(retv) = VT_BSTR;
358
        if(is_null_bstr(str)) {
359 360
            V_BSTR(retv) = NULL;
        }else {
361 362 363 364
            V_BSTR(retv) = SysAllocStringLen(NULL, jsstr_length(str));
            if(V_BSTR(retv))
                jsstr_flush(str, V_BSTR(retv));
            else
365 366
                return E_OUTOFMEMORY;
        }
367
        return S_OK;
368
    }
369 370 371 372 373 374 375 376 377 378 379
    case JSV_NUMBER: {
        double n = get_number(val);

        if(is_int32(n)) {
            V_VT(retv) = VT_I4;
            V_I4(retv) = n;
        }else {
            V_VT(retv) = VT_R8;
            V_R8(retv) = n;
        }

380
        return S_OK;
381
    }
382 383
    case JSV_BOOL:
        V_VT(retv) = VT_BOOL;
384
        V_BOOL(retv) = get_bool(val) ? VARIANT_TRUE : VARIANT_FALSE;
385 386
        return S_OK;
    case JSV_VARIANT:
387
        V_VT(retv) = VT_EMPTY;
388
        return VariantCopy(retv, get_variant(val));
389 390 391 392 393 394
    }

    assert(0);
    return E_FAIL;
}

395
/* ECMA-262 3rd Edition    9.1 */
396
HRESULT to_primitive(script_ctx_t *ctx, jsval_t val, jsval_t *ret, hint_t hint)
397
{
398
    if(is_object_instance(val)) {
399
        jsdisp_t *jsdisp;
400
        jsval_t prim;
401 402 403 404 405 406
        DISPID id;
        HRESULT hres;

        static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
        static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0};

407 408 409
        if(!get_object(val)) {
            *ret = jsval_null();
            return S_OK;
410 411
        }

412
        jsdisp = iface_to_jsdisp(get_object(val));
413
        if(!jsdisp)
414
            return disp_propget(ctx, get_object(val), DISPID_VALUE, ret);
415 416 417 418 419 420 421 422

        if(hint == NO_HINT)
            hint = is_class(jsdisp, JSCLASS_DATE) ? HINT_STRING : HINT_NUMBER;

        /* Native implementation doesn't throw TypeErrors, returns strange values */

        hres = jsdisp_get_id(jsdisp, hint == HINT_STRING ? toStringW : valueOfW, 0, &id);
        if(SUCCEEDED(hres)) {
423
            hres = jsdisp_call(jsdisp, id, DISPATCH_METHOD, 0, NULL, &prim);
424
            if(FAILED(hres)) {
425
                WARN("call error - forwarding exception\n");
426 427
                jsdisp_release(jsdisp);
                return hres;
428
            }else if(!is_object_instance(prim)) {
429
                jsdisp_release(jsdisp);
430
                *ret = prim;
431
                return S_OK;
432 433
            }else {
                IDispatch_Release(get_object(prim));
434 435 436 437 438
            }
        }

        hres = jsdisp_get_id(jsdisp, hint == HINT_STRING ? valueOfW : toStringW, 0, &id);
        if(SUCCEEDED(hres)) {
439
            hres = jsdisp_call(jsdisp, id, DISPATCH_METHOD, 0, NULL, &prim);
440
            if(FAILED(hres)) {
441
                WARN("call error - forwarding exception\n");
442 443
                jsdisp_release(jsdisp);
                return hres;
444
            }else if(!is_object_instance(prim)) {
445
                jsdisp_release(jsdisp);
446
                *ret = prim;
447
                return S_OK;
448 449
            }else {
                IDispatch_Release(get_object(prim));
450 451 452 453 454
            }
        }

        jsdisp_release(jsdisp);

455
        WARN("failed\n");
456
        return throw_type_error(ctx, JS_E_TO_PRIMITIVE, NULL);
457
    }
458 459 460 461 462

    return jsval_copy(val, ret);

}

463
/* ECMA-262 3rd Edition    9.2 */
464
HRESULT to_boolean(jsval_t val, BOOL *ret)
465
{
466
    switch(jsval_type(val)) {
467 468
    case JSV_UNDEFINED:
    case JSV_NULL:
469
        *ret = FALSE;
470 471 472 473 474
        return S_OK;
    case JSV_OBJECT:
        *ret = get_object(val) != NULL;
        return S_OK;
    case JSV_STRING:
475
        *ret = jsstr_length(get_string(val)) != 0;
476
        return S_OK;
477 478 479 480 481 482 483 484 485
    case JSV_NUMBER:
        *ret = !isnan(get_number(val)) && get_number(val);
        return S_OK;
    case JSV_BOOL:
        *ret = get_bool(val);
        return S_OK;
    case JSV_VARIANT:
        FIXME("unimplemented for variant %s\n", debugstr_variant(get_variant(val)));
        return E_NOTIMPL;
486 487
    }

488 489
    assert(0);
    return E_FAIL;
490 491
}

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
static int hex_to_int(WCHAR c)
{
    if('0' <= c && c <= '9')
        return c-'0';

    if('a' <= c && c <= 'f')
        return c-'a'+10;

    if('A' <= c && c <= 'F')
        return c-'A'+10;

    return -1;
}

/* ECMA-262 3rd Edition    9.3.1 */
507
static HRESULT str_to_number(jsstr_t *str, double *ret)
508
{
509
    const WCHAR *ptr;
510 511 512 513 514
    BOOL neg = FALSE;
    DOUBLE d = 0.0;

    static const WCHAR infinityW[] = {'I','n','f','i','n','i','t','y'};

515 516 517
    ptr = jsstr_flatten(str);
    if(!ptr)
        return E_OUTOFMEMORY;
518

519 520 521
    while(isspaceW(*ptr))
        ptr++;

522 523 524 525 526 527 528
    if(*ptr == '-') {
        neg = TRUE;
        ptr++;
    }else if(*ptr == '+') {
        ptr++;
    }

529 530
    if(!strncmpW(ptr, infinityW, ARRAY_SIZE(infinityW))) {
        ptr += ARRAY_SIZE(infinityW);
531 532 533
        while(*ptr && isspaceW(*ptr))
            ptr++;

534
        if(*ptr)
535
            *ret = NAN;
536
        else
537
            *ret = neg ? -INFINITY : INFINITY;
538
        return S_OK;
539 540
    }

541
    if(*ptr == '0' && ptr[1] == 'x') {
542 543 544 545 546 547 548 549
        DWORD l = 0;

        ptr += 2;
        while((l = hex_to_int(*ptr)) != -1) {
            d = d*16 + l;
            ptr++;
        }

550
        *ret = d;
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
        return S_OK;
    }

    while(isdigitW(*ptr))
        d = d*10 + (*ptr++ - '0');

    if(*ptr == 'e' || *ptr == 'E') {
        BOOL eneg = FALSE;
        LONG l = 0;

        ptr++;
        if(*ptr == '-') {
            ptr++;
            eneg = TRUE;
        }else if(*ptr == '+') {
            ptr++;
        }

        while(isdigitW(*ptr))
            l = l*10 + (*ptr++ - '0');
        if(eneg)
            l = -l;

        d *= pow(10, l);
    }else if(*ptr == '.') {
        DOUBLE dec = 0.1;

        ptr++;
        while(isdigitW(*ptr)) {
            d += dec * (*ptr++ - '0');
            dec *= 0.1;
        }
    }

    while(isspaceW(*ptr))
        ptr++;

    if(*ptr) {
589
        *ret = NAN;
590
        return S_OK;
591 592 593 594 595
    }

    if(neg)
        d = -d;

596
    *ret = d;
597 598 599
    return S_OK;
}

600
/* ECMA-262 3rd Edition    9.3 */
601
HRESULT to_number(script_ctx_t *ctx, jsval_t val, double *ret)
602
{
603
    switch(jsval_type(val)) {
604
    case JSV_UNDEFINED:
605
        *ret = NAN;
606 607
        return S_OK;
    case JSV_NULL:
608
        *ret = 0;
609 610 611 612 613 614 615
        return S_OK;
    case JSV_NUMBER:
        *ret = get_number(val);
        return S_OK;
    case JSV_STRING:
        return str_to_number(get_string(val), ret);
    case JSV_OBJECT: {
616
        jsval_t prim;
617 618
        HRESULT hres;

619
        hres = to_primitive(ctx, val, &prim, HINT_NUMBER);
620 621 622
        if(FAILED(hres))
            return hres;

623
        hres = to_number(ctx, prim, ret);
624
        jsval_release(prim);
625 626
        return hres;
    }
627 628
    case JSV_BOOL:
        *ret = get_bool(val) ? 1 : 0;
629
        return S_OK;
630 631 632 633
    case JSV_VARIANT:
        FIXME("unimplemented for variant %s\n", debugstr_variant(get_variant(val)));
        return E_NOTIMPL;
    };
634

635 636
    assert(0);
    return E_FAIL;
637 638
}

639
/* ECMA-262 3rd Edition    9.4 */
640
HRESULT to_integer(script_ctx_t *ctx, jsval_t v, double *ret)
641
{
642
    double n;
643 644
    HRESULT hres;

645
    hres = to_number(ctx, v, &n);
646 647 648
    if(FAILED(hres))
        return hres;

649 650 651 652
    if(isnan(n))
        *ret = 0;
    else
        *ret = n >= 0.0 ? floor(n) : -floor(-n);
653 654 655
    return S_OK;
}

656
/* ECMA-262 3rd Edition    9.5 */
657
HRESULT to_int32(script_ctx_t *ctx, jsval_t v, INT *ret)
658 659 660 661
{
    double n;
    HRESULT hres;

662 663
    const double p32 = (double)0xffffffff + 1;

664
    hres = to_number(ctx, v, &n);
665 666 667
    if(FAILED(hres))
        return hres;

668 669 670 671 672 673
    if(is_finite(n))
        n = n > 0 ? fmod(n, p32) : -fmod(-n, p32);
    else
        n = 0;

    *ret = (UINT32)n;
674 675 676
    return S_OK;
}

677
/* ECMA-262 3rd Edition    9.6 */
678
HRESULT to_uint32(script_ctx_t *ctx, jsval_t val, DWORD *ret)
679
{
680
    INT32 n;
681 682
    HRESULT hres;

683 684 685 686
    hres = to_int32(ctx, val, &n);
    if(SUCCEEDED(hres))
        *ret = n;
    return hres;
687 688
}

689
static jsstr_t *int_to_string(int i)
690 691 692 693 694 695
{
    WCHAR buf[12], *p;
    BOOL neg = FALSE;

    if(!i) {
        static const WCHAR zeroW[] = {'0',0};
696
        return jsstr_alloc(zeroW);
697 698 699 700 701 702 703
    }

    if(i < 0) {
        neg = TRUE;
        i = -i;
    }

704
    p = buf + ARRAY_SIZE(buf)-1;
705 706 707 708 709 710 711 712 713 714 715
    *p-- = 0;
    while(i) {
        *p-- = i%10 + '0';
        i /= 10;
    }

    if(neg)
        *p = '-';
    else
        p++;

716
    return jsstr_alloc(p);
717 718
}

719
HRESULT double_to_string(double n, jsstr_t **str)
720 721 722 723
{
    const WCHAR InfinityW[] = {'-','I','n','f','i','n','i','t','y',0};

    if(isnan(n)) {
724
        *str = jsstr_nan();
725
    }else if(isinf(n)) {
726
        *str = jsstr_alloc(n<0 ? InfinityW : InfinityW+1);
727
    }else if(is_int32(n)) {
728
        *str = int_to_string(n);
729 730 731 732
    }else {
        VARIANT strv, v;
        HRESULT hres;

733
        /* FIXME: Don't use VariantChangeTypeEx */
734 735 736 737 738 739 740
        V_VT(&v) = VT_R8;
        V_R8(&v) = n;
        V_VT(&strv) = VT_EMPTY;
        hres = VariantChangeTypeEx(&strv, &v, MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT), 0, VT_BSTR);
        if(FAILED(hres))
            return hres;

741 742
        *str = jsstr_alloc(V_BSTR(&strv));
        SysFreeString(V_BSTR(&strv));
743 744 745 746 747
    }

    return *str ? S_OK : E_OUTOFMEMORY;
}

748
/* ECMA-262 3rd Edition    9.8 */
749
HRESULT to_string(script_ctx_t *ctx, jsval_t val, jsstr_t **str)
750
{
751 752 753 754
    const WCHAR nullW[] = {'n','u','l','l',0};
    const WCHAR trueW[] = {'t','r','u','e',0};
    const WCHAR falseW[] = {'f','a','l','s','e',0};

755
    switch(jsval_type(val)) {
756
    case JSV_UNDEFINED:
757 758
        *str = jsstr_undefined();
        return S_OK;
759
    case JSV_NULL:
760
        *str = jsstr_alloc(nullW);
761
        break;
762
    case JSV_NUMBER:
763
        return double_to_string(get_number(val), str);
764
    case JSV_STRING:
765
        *str = jsstr_addref(get_string(val));
766
        break;
767
    case JSV_OBJECT: {
768
        jsval_t prim;
769
        HRESULT hres;
770

771
        hres = to_primitive(ctx, val, &prim, HINT_STRING);
772 773 774
        if(FAILED(hres))
            return hres;

775
        hres = to_string(ctx, prim, str);
776
        jsval_release(prim);
777 778
        return hres;
    }
779
    case JSV_BOOL:
780
        *str = jsstr_alloc(get_bool(val) ? trueW : falseW);
781
        break;
782
    default:
783
        FIXME("unsupported %s\n", debugstr_jsval(val));
784
        return E_NOTIMPL;
785 786
    }

787
    return *str ? S_OK : E_OUTOFMEMORY;
788 789
}

790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
HRESULT to_flat_string(script_ctx_t *ctx, jsval_t val, jsstr_t **str, const WCHAR **ret_str)
{
    HRESULT hres;

    hres = to_string(ctx, val, str);
    if(FAILED(hres))
        return hres;

    *ret_str = jsstr_flatten(*str);
    if(!*ret_str) {
        jsstr_release(*str);
        return E_OUTOFMEMORY;
    }

    return S_OK;
}

807
/* ECMA-262 3rd Edition    9.9 */
808
HRESULT to_object(script_ctx_t *ctx, jsval_t val, IDispatch **disp)
809
{
810
    jsdisp_t *dispex;
811 812
    HRESULT hres;

813
    switch(jsval_type(val)) {
814
    case JSV_STRING:
815
        hres = create_string(ctx, get_string(val), &dispex);
816 817 818
        if(FAILED(hres))
            return hres;

819
        *disp = to_disp(dispex);
820
        break;
821 822
    case JSV_NUMBER:
        hres = create_number(ctx, get_number(val), &dispex);
823 824 825
        if(FAILED(hres))
            return hres;

826
        *disp = to_disp(dispex);
827
        break;
828 829 830 831
    case JSV_OBJECT:
        if(get_object(val)) {
            *disp = get_object(val);
            IDispatch_AddRef(*disp);
832
        }else {
833
            jsdisp_t *obj;
834 835 836 837 838

            hres = create_object(ctx, NULL, &obj);
            if(FAILED(hres))
                return hres;

839
            *disp = to_disp(obj);
840
        }
841
        break;
842 843
    case JSV_BOOL:
        hres = create_bool(ctx, get_bool(val), &dispex);
844 845 846
        if(FAILED(hres))
            return hres;

847 848
        *disp = to_disp(dispex);
        break;
849 850 851 852
    case JSV_UNDEFINED:
    case JSV_NULL:
        WARN("object expected\n");
        return throw_type_error(ctx, JS_E_OBJECT_EXPECTED, NULL);
853 854 855 856 857 858
    case JSV_VARIANT:
        switch(V_VT(get_variant(val))) {
        case VT_ARRAY|VT_VARIANT:
            hres = create_vbarray(ctx, V_ARRAY(get_variant(val)), &dispex);
            if(FAILED(hres))
                return hres;
859

860 861 862 863 864 865 866
            *disp = to_disp(dispex);
            break;

        default:
            FIXME("Unsupported %s\n", debugstr_variant(get_variant(val)));
            return E_NOTIMPL;
        }
867
        break;
868 869 870 871
    }

    return S_OK;
}
872

873 874
HRESULT variant_change_type(script_ctx_t *ctx, VARIANT *dst, VARIANT *src, VARTYPE vt)
{
875
    jsval_t val;
876 877
    HRESULT hres;

878
    clear_ei(ctx);
879 880 881 882
    hres = variant_to_jsval(src, &val);
    if(FAILED(hres))
        return hres;

883 884 885 886 887
    switch(vt) {
    case VT_I2:
    case VT_I4: {
        INT i;

888
        hres = to_int32(ctx, val, &i);
889 890 891 892 893 894 895 896
        if(SUCCEEDED(hres)) {
            if(vt == VT_I4)
                V_I4(dst) = i;
            else
                V_I2(dst) = i;
        }
        break;
    }
897 898
    case VT_R8: {
        double n;
899
        hres = to_number(ctx, val, &n);
900 901
        if(SUCCEEDED(hres))
            V_R8(dst) = n;
902
        break;
903
    }
904
    case VT_R4: {
905
        double n;
906

907
        hres = to_number(ctx, val, &n);
908
        if(SUCCEEDED(hres))
909
            V_R4(dst) = n;
910 911 912
        break;
    }
    case VT_BOOL: {
913 914 915
        BOOL b;

        hres = to_boolean(val, &b);
916
        if(SUCCEEDED(hres))
917
            V_BOOL(dst) = b ? VARIANT_TRUE : VARIANT_FALSE;
918 919 920
        break;
    }
    case VT_BSTR: {
921
        jsstr_t *str;
922

923
        hres = to_string(ctx, val, &str);
924 925 926
        if(FAILED(hres))
            break;

927
        if(is_null_bstr(str)) {
928 929 930 931
            V_BSTR(dst) = NULL;
            break;
        }

932 933 934 935
        V_BSTR(dst) = SysAllocStringLen(NULL, jsstr_length(str));
        if(V_BSTR(dst))
            jsstr_flush(str, V_BSTR(dst));
        else
936
            hres = E_OUTOFMEMORY;
937 938 939 940 941 942 943 944 945 946 947 948 949
        break;
    }
    case VT_EMPTY:
        hres = V_VT(src) == VT_EMPTY ? S_OK : E_NOTIMPL;
        break;
    case VT_NULL:
        hres = V_VT(src) == VT_NULL ? S_OK : E_NOTIMPL;
        break;
    default:
        FIXME("vt %d not implemented\n", vt);
        hres = E_NOTIMPL;
    }

950
    jsval_release(val);
951
    if(FAILED(hres))
952 953 954 955 956 957
        return hres;

    V_VT(dst) = vt;
    return S_OK;
}

958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
static inline JSCaller *impl_from_IServiceProvider(IServiceProvider *iface)
{
    return CONTAINING_RECORD(iface, JSCaller, IServiceProvider_iface);
}

static HRESULT WINAPI JSCaller_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv)
{
    JSCaller *This = impl_from_IServiceProvider(iface);

    if(IsEqualGUID(&IID_IUnknown, riid)) {
        TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
        *ppv = &This->IServiceProvider_iface;
    }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
        TRACE("(%p)->(IID_IServiceProvider %p)\n", This, ppv);
        *ppv = &This->IServiceProvider_iface;
    }else {
        WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
}

static ULONG WINAPI JSCaller_AddRef(IServiceProvider *iface)
{
    JSCaller *This = impl_from_IServiceProvider(iface);
    LONG ref = InterlockedIncrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    return ref;
}

static ULONG WINAPI JSCaller_Release(IServiceProvider *iface)
{
    JSCaller *This = impl_from_IServiceProvider(iface);
    LONG ref = InterlockedIncrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

1000 1001
    if(!ref) {
        assert(!This->ctx);
1002
        heap_free(This);
1003
    }
1004 1005 1006 1007 1008 1009 1010 1011 1012

    return ref;
}

static HRESULT WINAPI JSCaller_QueryService(IServiceProvider *iface, REFGUID guidService,
        REFIID riid, void **ppv)
{
    JSCaller *This = impl_from_IServiceProvider(iface);

1013 1014 1015 1016 1017
    if(IsEqualGUID(guidService, &SID_VariantConversion) && This->ctx && This->ctx->active_script) {
        TRACE("(%p)->(SID_VariantConversion)\n", This);
        return IActiveScript_QueryInterface(This->ctx->active_script, riid, ppv);
    }

1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
    FIXME("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);

    *ppv = NULL;
    return E_NOINTERFACE;
}

static const IServiceProviderVtbl ServiceProviderVtbl = {
    JSCaller_QueryInterface,
    JSCaller_AddRef,
    JSCaller_Release,
    JSCaller_QueryService
};

HRESULT create_jscaller(script_ctx_t *ctx)
{
    JSCaller *ret;

    ret = heap_alloc(sizeof(*ret));
    if(!ret)
        return E_OUTOFMEMORY;

    ret->IServiceProvider_iface.lpVtbl = &ServiceProviderVtbl;
    ret->ref = 1;
1041
    ret->ctx = ctx;
1042 1043 1044 1045

    ctx->jscaller = ret;
    return S_OK;
}