jscript: Added Array.push 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
51 static HRESULT Array_length(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
52         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
53 {
54     ArrayInstance *This = (ArrayInstance*)dispex;
55
56     TRACE("%p %d\n", This, This->length);
57
58     switch(flags) {
59     case DISPATCH_PROPERTYGET:
60         V_VT(retv) = VT_I4;
61         V_I4(retv) = This->length;
62         break;
63     default:
64         FIXME("unimplemented flags %x\n", flags);
65         return E_NOTIMPL;
66     }
67
68     return S_OK;
69 }
70
71 static HRESULT Array_concat(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
72         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
73 {
74     FIXME("\n");
75     return E_NOTIMPL;
76 }
77
78 static HRESULT Array_join(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
79         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
80 {
81     FIXME("\n");
82     return E_NOTIMPL;
83 }
84
85 static HRESULT Array_pop(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
86         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
87 {
88     FIXME("\n");
89     return E_NOTIMPL;
90 }
91
92 /* ECMA-262 3rd Edition    15.4.4.7 */
93 static HRESULT Array_push(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
94         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
95 {
96     DWORD length = 0;
97     int i, n;
98     HRESULT hres;
99
100     TRACE("\n");
101
102     if(dispex->builtin_info->class == JSCLASS_ARRAY) {
103         length = ((ArrayInstance*)dispex)->length;
104     }else {
105         FIXME("not Array this\n");
106         return E_NOTIMPL;
107     }
108
109     n = dp->cArgs - dp->cNamedArgs;
110     for(i=0; i < n; i++) {
111         hres = jsdisp_propput_idx(dispex, length+i, lcid, get_arg(dp, i), ei, sp);
112         if(FAILED(hres))
113             return hres;
114     }
115
116     if(retv) {
117         V_VT(retv) = VT_I4;
118         V_I4(retv) = length+n;
119     }
120     return S_OK;
121 }
122
123 static HRESULT Array_reverse(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
124         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
125 {
126     FIXME("\n");
127     return E_NOTIMPL;
128 }
129
130 static HRESULT Array_shift(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
131         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
132 {
133     FIXME("\n");
134     return E_NOTIMPL;
135 }
136
137 static HRESULT Array_slice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
138         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
139 {
140     FIXME("\n");
141     return E_NOTIMPL;
142 }
143
144 static HRESULT Array_sort(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
145         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
146 {
147     FIXME("\n");
148     return E_NOTIMPL;
149 }
150
151 static HRESULT Array_splice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
152         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
153 {
154     FIXME("\n");
155     return E_NOTIMPL;
156 }
157
158 static HRESULT Array_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
159         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
160 {
161     FIXME("\n");
162     return E_NOTIMPL;
163 }
164
165 static HRESULT Array_toLocaleString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
166         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
167 {
168     FIXME("\n");
169     return E_NOTIMPL;
170 }
171
172 static HRESULT Array_valueOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
173         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
174 {
175     FIXME("\n");
176     return E_NOTIMPL;
177 }
178
179 static HRESULT Array_unshift(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
180         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
181 {
182     FIXME("\n");
183     return E_NOTIMPL;
184 }
185
186 static HRESULT Array_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
187         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
188 {
189     FIXME("\n");
190     return E_NOTIMPL;
191 }
192
193 static HRESULT Array_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
194         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
195 {
196     FIXME("\n");
197     return E_NOTIMPL;
198 }
199
200 static HRESULT Array_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
201         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
202 {
203     FIXME("\n");
204     return E_NOTIMPL;
205 }
206
207 static HRESULT Array_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
208         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
209 {
210     FIXME("\n");
211     return E_NOTIMPL;
212 }
213
214 static void Array_destructor(DispatchEx *dispex)
215 {
216     heap_free(dispex);
217 }
218
219 static void Array_on_put(DispatchEx *dispex, const WCHAR *name)
220 {
221     ArrayInstance *array = (ArrayInstance*)dispex;
222     const WCHAR *ptr = name;
223     DWORD id = 0;
224
225     if(!isdigitW(*ptr))
226         return;
227
228     while(*ptr && isdigitW(*ptr)) {
229         id = id*10 + (*ptr-'0');
230         ptr++;
231     }
232
233     if(*ptr)
234         return;
235
236     if(id >= array->length)
237         array->length = id+1;
238 }
239
240 static const builtin_prop_t Array_props[] = {
241     {concatW,                Array_concat,               PROPF_METHOD},
242     {hasOwnPropertyW,        Array_hasOwnProperty,       PROPF_METHOD},
243     {isPrototypeOfW,         Array_isPrototypeOf,        PROPF_METHOD},
244     {joinW,                  Array_join,                 PROPF_METHOD},
245     {lengthW,                Array_length,               0},
246     {popW,                   Array_pop,                  PROPF_METHOD},
247     {propertyIsEnumerableW,  Array_propertyIsEnumerable, PROPF_METHOD},
248     {pushW,                  Array_push,                 PROPF_METHOD},
249     {reverseW,               Array_reverse,              PROPF_METHOD},
250     {shiftW,                 Array_shift,                PROPF_METHOD},
251     {sliceW,                 Array_slice,                PROPF_METHOD},
252     {sortW,                  Array_sort,                 PROPF_METHOD},
253     {spliceW,                Array_splice,               PROPF_METHOD},
254     {toLocaleStringW,        Array_toLocaleString,       PROPF_METHOD},
255     {toStringW,              Array_toString,             PROPF_METHOD},
256     {unshiftW,               Array_unshift,              PROPF_METHOD},
257     {valueOfW,               Array_valueOf,              PROPF_METHOD}
258 };
259
260 static const builtin_info_t Array_info = {
261     JSCLASS_ARRAY,
262     {NULL, Array_value, 0},
263     sizeof(Array_props)/sizeof(*Array_props),
264     Array_props,
265     Array_destructor,
266     Array_on_put
267 };
268
269 static HRESULT ArrayConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
270         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
271 {
272     DispatchEx *obj;
273     VARIANT *arg_var;
274     DWORD i;
275     HRESULT hres;
276
277     TRACE("\n");
278
279     switch(flags) {
280     case DISPATCH_CONSTRUCT: {
281         if(arg_cnt(dp) == 1 && V_VT((arg_var = get_arg(dp, 0))) == VT_I4) {
282             if(V_I4(arg_var) < 0) {
283                 FIXME("throw RangeError\n");
284                 return E_FAIL;
285             }
286
287             hres = create_array(dispex->ctx, V_I4(arg_var), &obj);
288             if(FAILED(hres))
289                 return hres;
290
291             V_VT(retv) = VT_DISPATCH;
292             V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj);
293             return S_OK;
294         }
295
296         hres = create_array(dispex->ctx, arg_cnt(dp), &obj);
297         if(FAILED(hres))
298             return hres;
299
300         for(i=0; i < arg_cnt(dp); i++) {
301             hres = jsdisp_propput_idx(obj, i, lcid, get_arg(dp, i), ei, caller);
302             if(FAILED(hres))
303                 break;
304         }
305         if(FAILED(hres)) {
306             jsdisp_release(obj);
307             return hres;
308         }
309
310         V_VT(retv) = VT_DISPATCH;
311         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj);
312         break;
313     }
314     default:
315         FIXME("unimplemented flags: %x\n", flags);
316         return E_NOTIMPL;
317     }
318
319     return S_OK;
320 }
321
322 static HRESULT alloc_array(script_ctx_t *ctx, BOOL use_constr, ArrayInstance **ret)
323 {
324     ArrayInstance *array = heap_alloc_zero(sizeof(ArrayInstance));
325     HRESULT hres;
326
327     if(use_constr)
328         hres = init_dispex_from_constr(&array->dispex, ctx, &Array_info, ctx->array_constr);
329     else
330         hres = init_dispex(&array->dispex, ctx, &Array_info, NULL);
331
332     if(FAILED(hres)) {
333         heap_free(array);
334         return hres;
335     }
336
337     *ret = array;
338     return S_OK;
339 }
340
341 HRESULT create_array_constr(script_ctx_t *ctx, DispatchEx **ret)
342 {
343     ArrayInstance *array;
344     HRESULT hres;
345
346     hres = alloc_array(ctx, FALSE, &array);
347     if(FAILED(hres))
348         return hres;
349
350     hres = create_builtin_function(ctx, ArrayConstr_value, PROPF_CONSTR, &array->dispex, ret);
351
352     IDispatchEx_Release(_IDispatchEx_(&array->dispex));
353     return hres;
354 }
355
356 HRESULT create_array(script_ctx_t *ctx, DWORD length, DispatchEx **ret)
357 {
358     ArrayInstance *array;
359     HRESULT hres;
360
361     hres = alloc_array(ctx, TRUE, &array);
362     if(FAILED(hres))
363         return hres;
364
365     array->length = length;
366
367     *ret = &array->dispex;
368     return S_OK;
369 }