2005-07-17 Florian Gross <flgr@ccan.de>
[mono.git] / mcs / class / Microsoft.JScript / Microsoft.JScript / ArrayPrototype.cs
index 870f3a6b6ca3ed4c5b1c63a88d6df4f1612ccb0c..08760d648751477c53fba4c05f396d0dc9d7df43 100644 (file)
@@ -5,6 +5,7 @@
 //     Cesar Lopez Nataren (cesar@ciencias.unam.mx)
 //
 // (C) 2003, Cesar Lopez Nataren
+// (C) 2005, Novell Inc, (http://novell.com)
 //
 
 //
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-namespace Microsoft.JScript.Tmp
-{
-       using System;
-       using Microsoft.JScript.Vsa;
+using System;
+using System.Collections;
+using System.Text;
+using Microsoft.JScript.Vsa;
 
-       public class ArrayPrototype : ArrayObject
-       {
+namespace Microsoft.JScript {
+
+       public class ArrayPrototype : ArrayObject {
+               
+               [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject | JSFunctionAttributeEnum.HasEngine |
+                       JSFunctionAttributeEnum.HasVarArgs, JSBuiltin.Array_concat)]
                public static ArrayObject concat (object thisObj, VsaEngine engine,
                                                  params object [] args)
                {
-                       throw new NotImplementedException ();
-               }
+                       int i = 0;
+                       ArrayObject result = new ArrayObject ();
+                       int arg_idx = -1;
+                       int arg_count = args.Length;
 
+                       // TODO: Shouldn't this be generic!?
+                       SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
+                       object cur_obj = thisObj;
 
-               public static ArrayConstructor constructor {
-                       get { throw new NotImplementedException (); }
+                       ArrayObject cur_ary;
+                       while (cur_obj != null) {
+                               if (cur_obj is ArrayObject) {
+                                       cur_ary = (ArrayObject) cur_obj;
+
+                                       int n = (int) cur_ary.length;
+                                       for (int j = 0; j < n; j++, i++)
+                                               result.elems [i] = cur_ary.elems [j];
+                               } else
+                                       result.elems [i++] = cur_obj;
+
+                               arg_idx++;
+                               cur_obj = arg_idx < arg_count ? args [arg_idx] : null;
+                       }
+
+                       result.length = i;
+
+                       return result;
                }
 
+               public static ArrayConstructor constructor {
+                       get { return ArrayConstructor.Ctr; }
+               }
 
+               [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject, JSBuiltin.Array_join)]
                public static string join (object thisObj, object separator)
                {
-                       throw new NotImplementedException ();
-               }
+                       // TODO: Shouldn't this be generic!?
+                       SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
+                       ArrayObject array_obj = (ArrayObject) thisObj;
 
+                       string _separator;
+                       if (separator == null)
+                               _separator = ",";
+                       else
+                               _separator = Convert.ToString (separator);
+
+                       Hashtable elems = array_obj.elems;
+                       int n = (int) array_obj.length;
+                       StringBuilder str = new StringBuilder ();
+                       bool first = true;
+
+                       for (int i = 0; i < n; i++) {
+                               if (!first)
+                                       str.Append (_separator);
+                               first = false;
+                               object elem = elems [i];
+                               if (elem != null)
+                                       str.Append (Convert.ToString (elem));
+                       }
+                       return str.ToString ();
+               }
 
+               [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject, JSBuiltin.Array_pop)]
                public static object pop (object thisObj)
                {
-                       throw new NotImplementedException ();
-               }
+                       // TODO: Shouldn't this be generic!?
+                       SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
+                       ArrayObject array_obj = (ArrayObject) thisObj;
+                       Hashtable elems = array_obj.elems;
 
+                       int n = (int) array_obj.length;
+                       if (n > 0) {
+                               int new_len = n - 1;
+                               array_obj.length = new_len;
+                               if (elems.ContainsKey (new_len)) {
+                                       object result = elems [new_len];
+                                       elems.Remove (new_len);
+                                       return result;
+                               }
+                       }
+                       return null;
+               }
 
+               [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject | JSFunctionAttributeEnum.HasVarArgs, JSBuiltin.Array_push)]
                public static long push (object thisObj, params object [] args)
                {
-                       throw new NotImplementedException ();
-               }
+                       // TODO: Shouldn't this be generic!?
+                       SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
+                       ArrayObject array_obj = (ArrayObject) thisObj;
+                       Hashtable elems = array_obj.elems;
+
+                       int i = (int) array_obj.length;
+                       int n = i + args.Length;
+
+                       for (int j = 0; i < n; i++, j++)
+                               elems [i] = args [j];
 
+                       array_obj.length = n;
+                       return n;
+               }
 
+               [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject, JSBuiltin.Array_reverse)]
                public static object reverse (object thisObj)
                {
-                       throw new NotImplementedException ();
-               }
+                       // TODO: Shouldn't this be generic!?
+                       SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
+                       ArrayObject array_obj = (ArrayObject) thisObj;
+                       Hashtable elems = array_obj.elems;
 
+                       int n = (int) array_obj.length;
+                       int half_n = n / 2;
+                       int j = n - 1;
+                       object temp;
+                       
+                       for (int i = 0; i < half_n; i++, j--) {
+                               temp = elems [i];
+                               elems [i] = elems [j];
+                               elems [j] = temp;
+                       }
 
+                       return array_obj;
+               }
+
+               [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject, JSBuiltin.Array_shift)]
                public static object shift (object thisObj)
                {
-                       throw new NotImplementedException ();
-               }
+                       // TODO: Shouldn't this be generic!?
+                       SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
+                       ArrayObject array_obj = (ArrayObject) thisObj;
+                       Hashtable elems = array_obj.elems;
 
+                       int n = (int) array_obj.length;
+                       if (n > 0) {
+                               array_obj.length = n - 1;
+                               if (elems.ContainsKey (0)) {
+                                       object result = elems [0];
+                                       elems.Remove (0);
+                                       for (int i = 1; i < n; i++)
+                                               elems [i - 1] = elems [i];
+                                       elems.Remove (n - 1);
+                                       return result;
+                               }
+                       }
+                       return null;
+               }
 
-               public static ArrayObject slice (object thisObj, VsaEngine engine,
-                                                double start, object end)
+               [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject | JSFunctionAttributeEnum.HasEngine, JSBuiltin.Array_slice)]
+               public static ArrayObject slice (object thisObj, VsaEngine engine, double start, object end)
                {
-                       throw new NotImplementedException ();
-               }
+                       // TODO: Shouldn't this be generic!?
+                       SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
+                       ArrayObject array_obj = (ArrayObject) thisObj;
+                       int array_len = (int) array_obj.length;
+                       int _start, _end;
+
+                       if (start > array_len)
+                               _start = array_len;
+                       else {
+                               _start = (int) start;
+                               if (_start < 0)
+                                       _start += array_len;
+                       }
+
+                       if (end == null)
+                               _end = array_len;
+                       else {
+                               _end = Convert.ToInt32 (end);
+
+                               if (_end < 0)
+                                       _end += array_len;
+                               else if (_end > array_len)
+                                       _end = array_len;
+                       }
+
+                       if (_end < _start)
+                               _end = _start;
+
+                       ArrayObject result = new ArrayObject();
+                       result.length = _end - _start;
 
+                       for (int i = _start; i < _end; i++)
+                               result.elems [i - _start] = array_obj.elems [i];
 
+                       return result;
+               }
+
+               [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject, JSBuiltin.Array_sort)]
                public static object sort (object thisObj, object function)
                {
                        throw new NotImplementedException ();
                }
 
-
+               [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject | JSFunctionAttributeEnum.HasVarArgs | JSFunctionAttributeEnum.HasEngine, JSBuiltin.Array_splice)]
                public static ArrayObject splice (object thisObj, VsaEngine engine,
                                                  double start, double deleteCnt, 
                                                  params object [] args)
                {
-                       throw new NotImplementedException ();
-               }
+                       // TODO: Shouldn't this be generic!?
+                       SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
+                       ArrayObject array_obj = (ArrayObject) thisObj;
+                       ArrayObject result = new ArrayObject ();
+                       Hashtable elems = array_obj.elems;
+                       Hashtable del_elems = result.elems;
+
+                       int old_length = (int) array_obj.length;
+                       start = (int) start;
+                       if (start < 0)
+                               start = Math.Max (start + old_length, 0);
+                       else
+                               start = Math.Min (old_length, start);
+
+                       deleteCnt = (int) deleteCnt;
+                       deleteCnt = Math.Min (Math.Max (deleteCnt, 0), old_length - start);
 
+                       int arg_length = args.Length;
+                       int add_length = arg_length - (int) deleteCnt;
+                       add_length = Math.Max (add_length, -old_length);
+                       int del_length = -add_length;
+                       int new_length = old_length + add_length;
 
+                       int i, j, m;
+                       // First let's make some free space for the new items (if needed)
+                       if (add_length > 0) {
+                               i = old_length - 1;
+                               j = i + add_length;
+                               for (; i >= start; i--, j--)
+                                       elems [j] = elems [i];
+                       }
+
+                       // Then insert the new items in the now free space / replace existing ones
+                       j = m = 0;
+                       int old_start = (int) start + add_length;
+                       for (i = (int) start; j < arg_length; i++, j++) {
+                               if (i >= old_start && elems.ContainsKey (i)) {
+                                       del_elems [m] = elems [i];
+                                       m++;
+                                       elems.Remove (i);
+                               }
+
+                               if (j < arg_length)
+                                       elems [i] = args [j];
+                       }
+
+                       // Finally, delete elements which have no replacement elements
+                       if (add_length < 0) {
+                               int last_elem_idx = i + del_length;
+                               for (int k = 0; k < del_length; i++, j++, k++) {
+                                       if (elems.ContainsKey (i)) {
+                                               del_elems [m] = elems [i];
+                                               m++;
+                                               elems.Remove (i);
+                                       }
+                               }
+
+                               // And move up trailing elements
+                               int l = last_elem_idx - del_length;
+                               for (int k = last_elem_idx; l < old_length; k++, l++) {
+                                       if (elems.ContainsKey (k))
+                                               elems [l] = elems [k];
+                                       else if (elems.ContainsKey (l))
+                                               elems.Remove (l);
+                               }
+                       }
+
+                       array_obj.length = new_length;
+                       result.length = (int) deleteCnt;
+                       return result;
+               }
+
+               [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject, JSBuiltin.Array_toLocaleString)]
                public static string toLocaleString (object thisObj)
                {
                        throw new NotImplementedException ();
                }
 
-
-               public static string ToString (object thisObj)
+               [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject, JSBuiltin.Array_toString)]
+               public static string toString (object thisObj)
                {
-                       throw new NotImplementedException ();
+                       SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
+                       return ArrayPrototype.join (thisObj, null);
                }
 
-
+               [JSFunctionAttribute (JSFunctionAttributeEnum.HasThisObject | JSFunctionAttributeEnum.HasVarArgs, JSBuiltin.Array_unshift)]
                public static object unshift (object thisObj, params object [] args)
                {
-                       throw new NotImplementedException ();
+                       // TODO: Shouldn't this be generic!?
+                       SemanticAnalyser.assert_type (thisObj, typeof (ArrayObject));
+                       ArrayObject array_obj = (ArrayObject) thisObj;
+                       Hashtable elems = array_obj.elems;
+
+                       int old_length = (int) array_obj.length;
+                       int arg_length = args.Length;
+                       int new_length = old_length + arg_length;
+
+                       if (arg_length > 0) {
+                               // First let's make some free space for the new items
+                               int i = old_length - 1;
+                               int j = i + arg_length;
+                               for (; i >= 0; i--, j--)
+                                       elems [j] = elems [i];
+
+                               // Then insert the new items in the now free space
+                               for (; j >= 0; j--)
+                                       elems [j] = args [j];
+                       }
+                       //
+                       // NOTE: MSC returns the new array, but
+                       // ECMA-262 says to return the new length. We
+                       // conform to the standard.
+                       //
+                       array_obj.length = new_length;
+                       return new_length;
                }
        }
-}
\ No newline at end of file
+}