2005-07-17 Florian Gross <flgr@ccan.de>
[mono.git] / mcs / class / Microsoft.JScript / Microsoft.JScript / ArrayPrototype.cs
1 //
2 // ArrayPrototype.cs:
3 //
4 // Author:
5 //      Cesar Lopez Nataren (cesar@ciencias.unam.mx)
6 //
7 // (C) 2003, Cesar Lopez Nataren
8 // (C) 2005, Novell Inc, (http://novell.com)
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Collections;
34 using System.Text;
35 using Microsoft.JScript.Vsa;
36
37 namespace Microsoft.JScript {
38
39         public class ArrayPrototype : ArrayObject {
40                 
41                 [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject | JSFunctionAttributeEnum.HasEngine |
42                         JSFunctionAttributeEnum.HasVarArgs, JSBuiltin.Array_concat)]
43                 public static ArrayObject concat (object thisObj, VsaEngine engine,
44                                                   params object [] args)
45                 {
46                         int i = 0;
47                         ArrayObject result = new ArrayObject ();
48                         int arg_idx = -1;
49                         int arg_count = args.Length;
50
51                         // TODO: Shouldn't this be generic!?
52                         SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
53                         object cur_obj = thisObj;
54
55                         ArrayObject cur_ary;
56                         while (cur_obj != null) {
57                                 if (cur_obj is ArrayObject) {
58                                         cur_ary = (ArrayObject) cur_obj;
59
60                                         int n = (int) cur_ary.length;
61                                         for (int j = 0; j < n; j++, i++)
62                                                 result.elems [i] = cur_ary.elems [j];
63                                 } else
64                                         result.elems [i++] = cur_obj;
65
66                                 arg_idx++;
67                                 cur_obj = arg_idx < arg_count ? args [arg_idx] : null;
68                         }
69
70                         result.length = i;
71
72                         return result;
73                 }
74
75                 public static ArrayConstructor constructor {
76                         get { return ArrayConstructor.Ctr; }
77                 }
78
79                 [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject, JSBuiltin.Array_join)]
80                 public static string join (object thisObj, object separator)
81                 {
82                         // TODO: Shouldn't this be generic!?
83                         SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
84                         ArrayObject array_obj = (ArrayObject) thisObj;
85
86                         string _separator;
87                         if (separator == null)
88                                 _separator = ",";
89                         else
90                                 _separator = Convert.ToString (separator);
91
92                         Hashtable elems = array_obj.elems;
93                         int n = (int) array_obj.length;
94                         StringBuilder str = new StringBuilder ();
95                         bool first = true;
96
97                         for (int i = 0; i < n; i++) {
98                                 if (!first)
99                                         str.Append (_separator);
100                                 first = false;
101                                 object elem = elems [i];
102                                 if (elem != null)
103                                         str.Append (Convert.ToString (elem));
104                         }
105                         return str.ToString ();
106                 }
107
108                 [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject, JSBuiltin.Array_pop)]
109                 public static object pop (object thisObj)
110                 {
111                         // TODO: Shouldn't this be generic!?
112                         SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
113                         ArrayObject array_obj = (ArrayObject) thisObj;
114                         Hashtable elems = array_obj.elems;
115
116                         int n = (int) array_obj.length;
117                         if (n > 0) {
118                                 int new_len = n - 1;
119                                 array_obj.length = new_len;
120                                 if (elems.ContainsKey (new_len)) {
121                                         object result = elems [new_len];
122                                         elems.Remove (new_len);
123                                         return result;
124                                 }
125                         }
126                         return null;
127                 }
128
129                 [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject | JSFunctionAttributeEnum.HasVarArgs, JSBuiltin.Array_push)]
130                 public static long push (object thisObj, params object [] args)
131                 {
132                         // TODO: Shouldn't this be generic!?
133                         SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
134                         ArrayObject array_obj = (ArrayObject) thisObj;
135                         Hashtable elems = array_obj.elems;
136
137                         int i = (int) array_obj.length;
138                         int n = i + args.Length;
139
140                         for (int j = 0; i < n; i++, j++)
141                                 elems [i] = args [j];
142
143                         array_obj.length = n;
144                         return n;
145                 }
146
147                 [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject, JSBuiltin.Array_reverse)]
148                 public static object reverse (object thisObj)
149                 {
150                         // TODO: Shouldn't this be generic!?
151                         SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
152                         ArrayObject array_obj = (ArrayObject) thisObj;
153                         Hashtable elems = array_obj.elems;
154
155                         int n = (int) array_obj.length;
156                         int half_n = n / 2;
157                         int j = n - 1;
158                         object temp;
159                         
160                         for (int i = 0; i < half_n; i++, j--) {
161                                 temp = elems [i];
162                                 elems [i] = elems [j];
163                                 elems [j] = temp;
164                         }
165
166                         return array_obj;
167                 }
168
169                 [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject, JSBuiltin.Array_shift)]
170                 public static object shift (object thisObj)
171                 {
172                         // TODO: Shouldn't this be generic!?
173                         SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
174                         ArrayObject array_obj = (ArrayObject) thisObj;
175                         Hashtable elems = array_obj.elems;
176
177                         int n = (int) array_obj.length;
178                         if (n > 0) {
179                                 array_obj.length = n - 1;
180                                 if (elems.ContainsKey (0)) {
181                                         object result = elems [0];
182                                         elems.Remove (0);
183                                         for (int i = 1; i < n; i++)
184                                                 elems [i - 1] = elems [i];
185                                         elems.Remove (n - 1);
186                                         return result;
187                                 }
188                         }
189                         return null;
190                 }
191
192                 [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject | JSFunctionAttributeEnum.HasEngine, JSBuiltin.Array_slice)]
193                 public static ArrayObject slice (object thisObj, VsaEngine engine, double start, object end)
194                 {
195                         // TODO: Shouldn't this be generic!?
196                         SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
197                         ArrayObject array_obj = (ArrayObject) thisObj;
198                         int array_len = (int) array_obj.length;
199                         int _start, _end;
200
201                         if (start > array_len)
202                                 _start = array_len;
203                         else {
204                                 _start = (int) start;
205                                 if (_start < 0)
206                                         _start += array_len;
207                         }
208
209                         if (end == null)
210                                 _end = array_len;
211                         else {
212                                 _end = Convert.ToInt32 (end);
213
214                                 if (_end < 0)
215                                         _end += array_len;
216                                 else if (_end > array_len)
217                                         _end = array_len;
218                         }
219
220                         if (_end < _start)
221                                 _end = _start;
222
223                         ArrayObject result = new ArrayObject();
224                         result.length = _end - _start;
225
226                         for (int i = _start; i < _end; i++)
227                                 result.elems [i - _start] = array_obj.elems [i];
228
229                         return result;
230                 }
231
232                 [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject, JSBuiltin.Array_sort)]
233                 public static object sort (object thisObj, object function)
234                 {
235                         throw new NotImplementedException ();
236                 }
237
238                 [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject | JSFunctionAttributeEnum.HasVarArgs | JSFunctionAttributeEnum.HasEngine, JSBuiltin.Array_splice)]
239                 public static ArrayObject splice (object thisObj, VsaEngine engine,
240                                                   double start, double deleteCnt, 
241                                                   params object [] args)
242                 {
243                         // TODO: Shouldn't this be generic!?
244                         SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
245                         ArrayObject array_obj = (ArrayObject) thisObj;
246                         ArrayObject result = new ArrayObject ();
247                         Hashtable elems = array_obj.elems;
248                         Hashtable del_elems = result.elems;
249
250                         int old_length = (int) array_obj.length;
251                         start = (int) start;
252                         if (start < 0)
253                                 start = Math.Max (start + old_length, 0);
254                         else
255                                 start = Math.Min (old_length, start);
256
257                         deleteCnt = (int) deleteCnt;
258                         deleteCnt = Math.Min (Math.Max (deleteCnt, 0), old_length - start);
259
260                         int arg_length = args.Length;
261                         int add_length = arg_length - (int) deleteCnt;
262                         add_length = Math.Max (add_length, -old_length);
263                         int del_length = -add_length;
264                         int new_length = old_length + add_length;
265
266                         int i, j, m;
267                         // First let's make some free space for the new items (if needed)
268                         if (add_length > 0) {
269                                 i = old_length - 1;
270                                 j = i + add_length;
271                                 for (; i >= start; i--, j--)
272                                         elems [j] = elems [i];
273                         }
274
275                         // Then insert the new items in the now free space / replace existing ones
276                         j = m = 0;
277                         int old_start = (int) start + add_length;
278                         for (i = (int) start; j < arg_length; i++, j++) {
279                                 if (i >= old_start && elems.ContainsKey (i)) {
280                                         del_elems [m] = elems [i];
281                                         m++;
282                                         elems.Remove (i);
283                                 }
284
285                                 if (j < arg_length)
286                                         elems [i] = args [j];
287                         }
288
289                         // Finally, delete elements which have no replacement elements
290                         if (add_length < 0) {
291                                 int last_elem_idx = i + del_length;
292                                 for (int k = 0; k < del_length; i++, j++, k++) {
293                                         if (elems.ContainsKey (i)) {
294                                                 del_elems [m] = elems [i];
295                                                 m++;
296                                                 elems.Remove (i);
297                                         }
298                                 }
299
300                                 // And move up trailing elements
301                                 int l = last_elem_idx - del_length;
302                                 for (int k = last_elem_idx; l < old_length; k++, l++) {
303                                         if (elems.ContainsKey (k))
304                                                 elems [l] = elems [k];
305                                         else if (elems.ContainsKey (l))
306                                                 elems.Remove (l);
307                                 }
308                         }
309
310                         array_obj.length = new_length;
311                         result.length = (int) deleteCnt;
312                         return result;
313                 }
314
315                 [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject, JSBuiltin.Array_toLocaleString)]
316                 public static string toLocaleString (object thisObj)
317                 {
318                         throw new NotImplementedException ();
319                 }
320
321                 [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject, JSBuiltin.Array_toString)]
322                 public static string toString (object thisObj)
323                 {
324                         SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
325                         return ArrayPrototype.join (thisObj, null);
326                 }
327
328                 [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject | JSFunctionAttributeEnum.HasVarArgs, JSBuiltin.Array_unshift)]
329                 public static object unshift (object thisObj, params object [] args)
330                 {
331                         // TODO: Shouldn't this be generic!?
332                         SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
333                         ArrayObject array_obj = (ArrayObject) thisObj;
334                         Hashtable elems = array_obj.elems;
335
336                         int old_length = (int) array_obj.length;
337                         int arg_length = args.Length;
338                         int new_length = old_length + arg_length;
339
340                         if (arg_length > 0) {
341                                 // First let's make some free space for the new items
342                                 int i = old_length - 1;
343                                 int j = i + arg_length;
344                                 for (; i >= 0; i--, j--)
345                                         elems [j] = elems [i];
346
347                                 // Then insert the new items in the now free space
348                                 for (; j >= 0; j--)
349                                         elems [j] = args [j];
350                         }
351                         //
352                         // NOTE: MSC returns the new array, but
353                         // ECMA-262 says to return the new length. We
354                         // conform to the standard.
355                         //
356                         array_obj.length = new_length;
357                         return new_length;
358                 }
359         }
360 }