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