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