jscript: Added Number function implementation.
[wine] / dlls / jscript / array.c
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 "jscript.h"
20
21 #include "wine/debug.h"
22
23 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
24
25 typedef struct {
26     DispatchEx dispex;
27
28     DWORD length;
29 } ArrayInstance;
30
31 static const WCHAR lengthW[] = {'l','e','n','g','t','h',0};
32 static const WCHAR concatW[] = {'c','o','n','c','a','t',0};
33 static const WCHAR joinW[] = {'j','o','i','n',0};
34 static const WCHAR popW[] = {'p','o','p',0};
35 static const WCHAR pushW[] = {'p','u','s','h',0};
36 static const WCHAR reverseW[] = {'r','e','v','e','r','s','e',0};
37 static const WCHAR shiftW[] = {'s','h','i','f','t',0};
38 static const WCHAR sliceW[] = {'s','l','i','c','e',0};
39 static const WCHAR sortW[] = {'s','o','r','t',0};
40 static const WCHAR spliceW[] = {'s','p','l','i','c','e',0};
41 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
42 static const WCHAR toLocaleStringW[] = {'t','o','L','o','c','a','l','e','S','t','r','i','n','g',0};
43 static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0};
44 static const WCHAR unshiftW[] = {'u','n','s','h','i','f','t',0};
45 static const WCHAR hasOwnPropertyW[] = {'h','a','s','O','w','n','P','r','o','p','e','r','t','y',0};
46 static const WCHAR propertyIsEnumerableW[] =
47     {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0};
48 static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0};
49
50 const WCHAR default_separatorW[] = {',',0};
51
52 static HRESULT Array_length(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
53         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
54 {
55     ArrayInstance *This = (ArrayInstance*)dispex;
56
57     TRACE("%p %d\n", This, This->length);
58
59     switch(flags) {
60     case DISPATCH_PROPERTYGET:
61         V_VT(retv) = VT_I4;
62         V_I4(retv) = This->length;
63         break;
64     default:
65         FIXME("unimplemented flags %x\n", flags);
66         return E_NOTIMPL;
67     }
68
69     return S_OK;
70 }
71
72 static HRESULT Array_concat(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
73         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
74 {
75     FIXME("\n");
76     return E_NOTIMPL;
77 }
78
79 static HRESULT array_join(DispatchEx *array, LCID lcid, DWORD length, const WCHAR *sep, VARIANT *retv,
80         jsexcept_t *ei, IServiceProvider *caller)
81 {
82     BSTR *str_tab, ret = NULL;
83     VARIANT var;
84     DWORD i;
85     HRESULT hres = E_FAIL;
86
87     if(!length) {
88         if(retv) {
89             V_VT(retv) = VT_BSTR;
90             V_BSTR(retv) = SysAllocStringLen(NULL, 0);
91             if(!V_BSTR(retv))
92                 return E_OUTOFMEMORY;
93         }
94         return S_OK;
95     }
96
97     str_tab = heap_alloc_zero(length * sizeof(BSTR));
98     if(!str_tab)
99         return E_OUTOFMEMORY;
100
101     for(i=0; i < length; i++) {
102         hres = jsdisp_propget_idx(array, i, lcid, &var, ei, caller);
103         if(FAILED(hres))
104             break;
105
106         if(V_VT(&var) != VT_EMPTY && V_VT(&var) != VT_NULL)
107             hres = to_string(array->ctx, &var, ei, str_tab+i);
108         VariantClear(&var);
109         if(FAILED(hres))
110             break;
111     }
112
113     if(SUCCEEDED(hres)) {
114         DWORD seplen = 0, len = 0;
115         WCHAR *ptr;
116
117         seplen = strlenW(sep);
118
119         if(str_tab[0])
120             len = SysStringLen(str_tab[0]);
121         for(i=1; i < length; i++)
122             len += seplen + SysStringLen(str_tab[i]);
123
124         ret = SysAllocStringLen(NULL, len);
125         if(ret) {
126             DWORD tmplen = 0;
127
128             if(str_tab[0]) {
129                 tmplen = SysStringLen(str_tab[0]);
130                 memcpy(ret, str_tab[0], tmplen*sizeof(WCHAR));
131             }
132
133             ptr = ret + tmplen;
134             for(i=1; i < length; i++) {
135                 if(seplen) {
136                     memcpy(ptr, sep, seplen*sizeof(WCHAR));
137                     ptr += seplen;
138                 }
139
140                 if(str_tab[i]) {
141                     tmplen = SysStringLen(str_tab[i]);
142                     memcpy(ptr, str_tab[i], tmplen*sizeof(WCHAR));
143                     ptr += tmplen;
144                 }
145             }
146             *ptr=0;
147         }else {
148             hres = E_OUTOFMEMORY;
149         }
150     }
151
152     for(i=0; i < length; i++)
153         SysFreeString(str_tab[i]);
154     heap_free(str_tab);
155     if(FAILED(hres))
156         return hres;
157
158     TRACE("= %s\n", debugstr_w(ret));
159
160     if(retv) {
161         if(!ret) {
162             ret = SysAllocStringLen(NULL, 0);
163             if(!ret)
164                 return E_OUTOFMEMORY;
165         }
166
167         V_VT(retv) = VT_BSTR;
168         V_BSTR(retv) = ret;
169     }else {
170         SysFreeString(ret);
171     }
172
173     return S_OK;
174 }
175
176 /* ECMA-262 3rd Edition    15.4.4.5 */
177 static HRESULT Array_join(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
178         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
179 {
180     DWORD length;
181     HRESULT hres;
182
183     TRACE("\n");
184
185     if(is_class(dispex, JSCLASS_ARRAY)) {
186         length = ((ArrayInstance*)dispex)->length;
187     }else {
188         FIXME("dispid is not Array\n");
189         return E_NOTIMPL;
190     }
191
192     if(arg_cnt(dp)) {
193         BSTR sep;
194
195         hres = to_string(dispex->ctx, dp->rgvarg + dp->cArgs-1, ei, &sep);
196         if(FAILED(hres))
197             return hres;
198
199         hres = array_join(dispex, lcid, length, sep, retv, ei, caller);
200
201         SysFreeString(sep);
202     }else {
203         hres = array_join(dispex, lcid, length, default_separatorW, retv, ei, caller);
204     }
205
206     return hres;
207 }
208
209 static HRESULT Array_pop(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
210         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
211 {
212     FIXME("\n");
213     return E_NOTIMPL;
214 }
215
216 /* ECMA-262 3rd Edition    15.4.4.7 */
217 static HRESULT Array_push(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
218         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
219 {
220     DWORD length = 0;
221     int i, n;
222     HRESULT hres;
223
224     TRACE("\n");
225
226     if(dispex->builtin_info->class == JSCLASS_ARRAY) {
227         length = ((ArrayInstance*)dispex)->length;
228     }else {
229         FIXME("not Array this\n");
230         return E_NOTIMPL;
231     }
232
233     n = dp->cArgs - dp->cNamedArgs;
234     for(i=0; i < n; i++) {
235         hres = jsdisp_propput_idx(dispex, length+i, lcid, get_arg(dp, i), ei, sp);
236         if(FAILED(hres))
237             return hres;
238     }
239
240     if(retv) {
241         V_VT(retv) = VT_I4;
242         V_I4(retv) = length+n;
243     }
244     return S_OK;
245 }
246
247 static HRESULT Array_reverse(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
248         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
249 {
250     FIXME("\n");
251     return E_NOTIMPL;
252 }
253
254 static HRESULT Array_shift(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
255         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
256 {
257     FIXME("\n");
258     return E_NOTIMPL;
259 }
260
261 static HRESULT Array_slice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
262         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
263 {
264     FIXME("\n");
265     return E_NOTIMPL;
266 }
267
268 static HRESULT Array_sort(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
269         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
270 {
271     FIXME("\n");
272     return E_NOTIMPL;
273 }
274
275 static HRESULT Array_splice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
276         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
277 {
278     FIXME("\n");
279     return E_NOTIMPL;
280 }
281
282 /* ECMA-262 3rd Edition    15.4.4.2 */
283 static HRESULT Array_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
284         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
285 {
286     TRACE("\n");
287
288     if(!is_class(dispex, JSCLASS_ARRAY)) {
289         WARN("not Array object\n");
290         return E_FAIL;
291     }
292
293     return array_join(dispex, lcid, ((ArrayInstance*)dispex)->length, default_separatorW, retv, ei, sp);
294 }
295
296 static HRESULT Array_toLocaleString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
297         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
298 {
299     FIXME("\n");
300     return E_NOTIMPL;
301 }
302
303 static HRESULT Array_valueOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
304         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
305 {
306     FIXME("\n");
307     return E_NOTIMPL;
308 }
309
310 static HRESULT Array_unshift(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
311         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
312 {
313     FIXME("\n");
314     return E_NOTIMPL;
315 }
316
317 static HRESULT Array_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
318         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
319 {
320     FIXME("\n");
321     return E_NOTIMPL;
322 }
323
324 static HRESULT Array_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
325         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
326 {
327     FIXME("\n");
328     return E_NOTIMPL;
329 }
330
331 static HRESULT Array_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
332         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
333 {
334     FIXME("\n");
335     return E_NOTIMPL;
336 }
337
338 static HRESULT Array_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
339         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
340 {
341     FIXME("\n");
342     return E_NOTIMPL;
343 }
344
345 static void Array_destructor(DispatchEx *dispex)
346 {
347     heap_free(dispex);
348 }
349
350 static void Array_on_put(DispatchEx *dispex, const WCHAR *name)
351 {
352     ArrayInstance *array = (ArrayInstance*)dispex;
353     const WCHAR *ptr = name;
354     DWORD id = 0;
355
356     if(!isdigitW(*ptr))
357         return;
358
359     while(*ptr && isdigitW(*ptr)) {
360         id = id*10 + (*ptr-'0');
361         ptr++;
362     }
363
364     if(*ptr)
365         return;
366
367     if(id >= array->length)
368         array->length = id+1;
369 }
370
371 static const builtin_prop_t Array_props[] = {
372     {concatW,                Array_concat,               PROPF_METHOD},
373     {hasOwnPropertyW,        Array_hasOwnProperty,       PROPF_METHOD},
374     {isPrototypeOfW,         Array_isPrototypeOf,        PROPF_METHOD},
375     {joinW,                  Array_join,                 PROPF_METHOD},
376     {lengthW,                Array_length,               0},
377     {popW,                   Array_pop,                  PROPF_METHOD},
378     {propertyIsEnumerableW,  Array_propertyIsEnumerable, PROPF_METHOD},
379     {pushW,                  Array_push,                 PROPF_METHOD},
380     {reverseW,               Array_reverse,              PROPF_METHOD},
381     {shiftW,                 Array_shift,                PROPF_METHOD},
382     {sliceW,                 Array_slice,                PROPF_METHOD},
383     {sortW,                  Array_sort,                 PROPF_METHOD},
384     {spliceW,                Array_splice,               PROPF_METHOD},
385     {toLocaleStringW,        Array_toLocaleString,       PROPF_METHOD},
386     {toStringW,              Array_toString,             PROPF_METHOD},
387     {unshiftW,               Array_unshift,              PROPF_METHOD},
388     {valueOfW,               Array_valueOf,              PROPF_METHOD}
389 };
390
391 static const builtin_info_t Array_info = {
392     JSCLASS_ARRAY,
393     {NULL, Array_value, 0},
394     sizeof(Array_props)/sizeof(*Array_props),
395     Array_props,
396     Array_destructor,
397     Array_on_put
398 };
399
400 static HRESULT ArrayConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
401         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
402 {
403     DispatchEx *obj;
404     VARIANT *arg_var;
405     DWORD i;
406     HRESULT hres;
407
408     TRACE("\n");
409
410     switch(flags) {
411     case DISPATCH_CONSTRUCT: {
412         if(arg_cnt(dp) == 1 && V_VT((arg_var = get_arg(dp, 0))) == VT_I4) {
413             if(V_I4(arg_var) < 0) {
414                 FIXME("throw RangeError\n");
415                 return E_FAIL;
416             }
417
418             hres = create_array(dispex->ctx, V_I4(arg_var), &obj);
419             if(FAILED(hres))
420                 return hres;
421
422             V_VT(retv) = VT_DISPATCH;
423             V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj);
424             return S_OK;
425         }
426
427         hres = create_array(dispex->ctx, arg_cnt(dp), &obj);
428         if(FAILED(hres))
429             return hres;
430
431         for(i=0; i < arg_cnt(dp); i++) {
432             hres = jsdisp_propput_idx(obj, i, lcid, get_arg(dp, i), ei, caller);
433             if(FAILED(hres))
434                 break;
435         }
436         if(FAILED(hres)) {
437             jsdisp_release(obj);
438             return hres;
439         }
440
441         V_VT(retv) = VT_DISPATCH;
442         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj);
443         break;
444     }
445     default:
446         FIXME("unimplemented flags: %x\n", flags);
447         return E_NOTIMPL;
448     }
449
450     return S_OK;
451 }
452
453 static HRESULT alloc_array(script_ctx_t *ctx, BOOL use_constr, ArrayInstance **ret)
454 {
455     ArrayInstance *array = heap_alloc_zero(sizeof(ArrayInstance));
456     HRESULT hres;
457
458     if(use_constr)
459         hres = init_dispex_from_constr(&array->dispex, ctx, &Array_info, ctx->array_constr);
460     else
461         hres = init_dispex(&array->dispex, ctx, &Array_info, NULL);
462
463     if(FAILED(hres)) {
464         heap_free(array);
465         return hres;
466     }
467
468     *ret = array;
469     return S_OK;
470 }
471
472 HRESULT create_array_constr(script_ctx_t *ctx, DispatchEx **ret)
473 {
474     ArrayInstance *array;
475     HRESULT hres;
476
477     hres = alloc_array(ctx, FALSE, &array);
478     if(FAILED(hres))
479         return hres;
480
481     hres = create_builtin_function(ctx, ArrayConstr_value, PROPF_CONSTR, &array->dispex, ret);
482
483     IDispatchEx_Release(_IDispatchEx_(&array->dispex));
484     return hres;
485 }
486
487 HRESULT create_array(script_ctx_t *ctx, DWORD length, DispatchEx **ret)
488 {
489     ArrayInstance *array;
490     HRESULT hres;
491
492     hres = alloc_array(ctx, TRUE, &array);
493     if(FAILED(hres))
494         return hres;
495
496     array->length = length;
497
498     *ret = &array->dispex;
499     return S_OK;
500 }