jscript: Fixed Object.toLocaleString implementation.
[wine] / dlls / jscript / number.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 <math.h>
20
21 #include "jscript.h"
22
23 #include "wine/debug.h"
24
25 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
26
27 typedef struct {
28     DispatchEx dispex;
29
30     VARIANT num;
31 } NumberInstance;
32
33 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
34 static const WCHAR toLocaleStringW[] = {'t','o','L','o','c','a','l','e','S','t','r','i','n','g',0};
35 static const WCHAR toFixedW[] = {'t','o','F','i','x','e','d',0};
36 static const WCHAR toExponentialW[] = {'t','o','E','x','p','o','n','e','n','t','i','a','l',0};
37 static const WCHAR toPrecisionW[] = {'t','o','P','r','e','c','i','s','i','o','n',0};
38 static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0};
39
40 #define NUMBER_TOSTRING_BUF_SIZE 64
41 /* ECMA-262 3rd Edition    15.7.4.2 */
42 static HRESULT Number_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
43         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
44 {
45     NumberInstance *number;
46     INT radix = 10;
47     DOUBLE val;
48     BSTR str;
49     HRESULT hres;
50
51     TRACE("\n");
52
53     if(!is_class(dispex, JSCLASS_NUMBER))
54         return throw_type_error(dispex->ctx, ei, IDS_NOT_NUM, NULL);
55
56     number = (NumberInstance*)dispex;
57
58     if(arg_cnt(dp)) {
59         hres = to_int32(dispex->ctx, get_arg(dp, 0), ei, &radix);
60         if(FAILED(hres))
61             return hres;
62
63         if(radix<2 || radix>36)
64             return throw_type_error(dispex->ctx, ei, IDS_INVALID_CALL_ARG, NULL);
65     }
66
67     if(V_VT(&number->num) == VT_I4)
68         val = V_I4(&number->num);
69     else
70         val = V_R8(&number->num);
71
72     if(radix==10 || isnan(val) || isinf(val)) {
73         hres = to_string(dispex->ctx, &number->num, ei, &str);
74         if(FAILED(hres))
75             return hres;
76     }
77     else {
78         INT idx = 0;
79         DOUBLE integ, frac, log_radix = 0;
80         WCHAR buf[NUMBER_TOSTRING_BUF_SIZE+16];
81         BOOL exp = FALSE;
82
83         if(val<0) {
84             val = -val;
85             buf[idx++] = '-';
86         }
87
88         while(1) {
89             integ = floor(val);
90             frac = val-integ;
91
92             if(integ == 0)
93                 buf[idx++] = '0';
94             while(integ>=1 && idx<NUMBER_TOSTRING_BUF_SIZE) {
95                 buf[idx] = fmod(integ, radix);
96                 if(buf[idx]<10) buf[idx] += '0';
97                 else buf[idx] += 'a'-10;
98                 integ /= radix;
99                 idx++;
100             }
101
102             if(idx<NUMBER_TOSTRING_BUF_SIZE) {
103                 INT beg = buf[0]=='-'?1:0;
104                 INT end = idx-1;
105                 WCHAR wch;
106
107                 while(end > beg) {
108                     wch = buf[beg];
109                     buf[beg++] = buf[end];
110                     buf[end--] = wch;
111                 }
112             }
113
114             if(idx != NUMBER_TOSTRING_BUF_SIZE) buf[idx++] = '.';
115
116             while(frac>0 && idx<NUMBER_TOSTRING_BUF_SIZE) {
117                 frac *= radix;
118                 buf[idx] = fmod(frac, radix);
119                 frac -= buf[idx];
120                 if(buf[idx]<10) buf[idx] += '0';
121                 else buf[idx] += 'a'-10;
122                 idx++;
123             }
124
125             if(idx==NUMBER_TOSTRING_BUF_SIZE && !exp) {
126                 exp = TRUE;
127                 idx = (buf[0]=='-') ? 1 : 0;
128                 log_radix = floor(log(val)/log(radix));
129                 val *= pow(radix, -log_radix);
130                 continue;
131             }
132
133             break;
134         }
135
136         while(buf[idx-1] == '0') idx--;
137         if(buf[idx-1] == '.') idx--;
138
139         if(exp) {
140             if(log_radix==0)
141                 buf[idx++] = '\0';
142             else {
143                 static const WCHAR formatW[] = {'(','e','%','c','%','d',')',0};
144                 WCHAR ch;
145
146                 if(log_radix<0) {
147                     log_radix = -log_radix;
148                     ch = '-';
149                 }
150                 else ch = '+';
151                 sprintfW(&buf[idx], formatW, ch, (int)log_radix);
152             }
153         }
154         else buf[idx] = '\0';
155
156         str = SysAllocString(buf);
157         if(!str)
158             return E_OUTOFMEMORY;
159     }
160
161     if(retv) {
162         V_VT(retv) = VT_BSTR;
163         V_BSTR(retv) = str;
164     }else {
165         SysFreeString(str);
166     }
167     return S_OK;
168 }
169
170 static HRESULT Number_toLocaleString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
171         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
172 {
173     FIXME("\n");
174     return E_NOTIMPL;
175 }
176
177 static HRESULT Number_toFixed(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
178         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
179 {
180     FIXME("\n");
181     return E_NOTIMPL;
182 }
183
184 static HRESULT Number_toExponential(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
185         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
186 {
187     FIXME("\n");
188     return E_NOTIMPL;
189 }
190
191 static HRESULT Number_toPrecision(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
192         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
193 {
194     FIXME("\n");
195     return E_NOTIMPL;
196 }
197
198 static HRESULT Number_valueOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
199         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
200 {
201     TRACE("\n");
202
203     if(!is_class(dispex, JSCLASS_NUMBER))
204         return throw_type_error(dispex->ctx, ei, IDS_NOT_NUM, NULL);
205
206     if(retv) {
207         NumberInstance *number = (NumberInstance*)dispex;
208         *retv = number->num;
209     }
210     return S_OK;
211 }
212
213 static HRESULT Number_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
214         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
215 {
216     NumberInstance *number = (NumberInstance*)dispex;
217
218     switch(flags) {
219     case INVOKE_FUNC:
220         return throw_type_error(dispex->ctx, ei, IDS_NOT_FUNC, NULL);
221     case DISPATCH_PROPERTYGET:
222         *retv = number->num;
223         break;
224
225     default:
226         FIXME("flags %x\n", flags);
227         return E_NOTIMPL;
228     }
229
230     return S_OK;
231 }
232
233 static const builtin_prop_t Number_props[] = {
234     {toExponentialW,         Number_toExponential,         PROPF_METHOD},
235     {toFixedW,               Number_toFixed,               PROPF_METHOD},
236     {toLocaleStringW,        Number_toLocaleString,        PROPF_METHOD},
237     {toPrecisionW,           Number_toPrecision,           PROPF_METHOD},
238     {toStringW,              Number_toString,              PROPF_METHOD},
239     {valueOfW,               Number_valueOf,               PROPF_METHOD}
240 };
241
242 static const builtin_info_t Number_info = {
243     JSCLASS_NUMBER,
244     {NULL, Number_value, 0},
245     sizeof(Number_props)/sizeof(*Number_props),
246     Number_props,
247     NULL,
248     NULL
249 };
250
251 static HRESULT NumberConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
252         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
253 {
254     VARIANT num;
255     HRESULT hres;
256
257     TRACE("\n");
258
259     switch(flags) {
260     case INVOKE_FUNC:
261         if(!arg_cnt(dp)) {
262             if(retv) {
263                 V_VT(retv) = VT_I4;
264                 V_I4(retv) = 0;
265             }
266             return S_OK;
267         }
268
269         hres = to_number(dispex->ctx, get_arg(dp, 0), ei, &num);
270         if(FAILED(hres))
271             return hres;
272
273         if(retv)
274             *retv = num;
275         break;
276
277     case DISPATCH_CONSTRUCT: {
278         DispatchEx *obj;
279
280         if(arg_cnt(dp)) {
281             hres = to_number(dispex->ctx, get_arg(dp, 0), ei, &num);
282             if(FAILED(hres))
283                 return hres;
284         }else {
285             V_VT(&num) = VT_I4;
286             V_I4(&num) = 0;
287         }
288
289         hres = create_number(dispex->ctx, &num, &obj);
290         if(FAILED(hres))
291             return hres;
292
293         V_VT(retv) = VT_DISPATCH;
294         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj);
295         break;
296     }
297     default:
298         FIXME("unimplemented flags %x\n", flags);
299         return E_NOTIMPL;
300     }
301
302     return S_OK;
303 }
304
305 static HRESULT alloc_number(script_ctx_t *ctx, DispatchEx *object_prototype, NumberInstance **ret)
306 {
307     NumberInstance *number;
308     HRESULT hres;
309
310     number = heap_alloc_zero(sizeof(NumberInstance));
311     if(!number)
312         return E_OUTOFMEMORY;
313
314     if(object_prototype)
315         hres = init_dispex(&number->dispex, ctx, &Number_info, object_prototype);
316     else
317         hres = init_dispex_from_constr(&number->dispex, ctx, &Number_info, ctx->number_constr);
318     if(FAILED(hres))
319         return hres;
320
321     *ret = number;
322     return S_OK;
323 }
324
325 HRESULT create_number_constr(script_ctx_t *ctx, DispatchEx *object_prototype, DispatchEx **ret)
326 {
327     NumberInstance *number;
328     HRESULT hres;
329
330     hres = alloc_number(ctx, object_prototype, &number);
331     if(FAILED(hres))
332         return hres;
333
334     V_VT(&number->num) = VT_I4;
335     hres = create_builtin_function(ctx, NumberConstr_value, NULL, PROPF_CONSTR, &number->dispex, ret);
336
337     jsdisp_release(&number->dispex);
338     return hres;
339 }
340
341 HRESULT create_number(script_ctx_t *ctx, VARIANT *num, DispatchEx **ret)
342 {
343     NumberInstance *number;
344     HRESULT hres;
345
346     hres = alloc_number(ctx, NULL, &number);
347     if(FAILED(hres))
348         return hres;
349
350     number->num = *num;
351
352     *ret = &number->dispex;
353     return S_OK;
354 }