kjs Library API Documentation

array_object.cpp

00001 // -*- c-basic-offset: 2 -*-
00002 /*
00003  *  This file is part of the KDE libraries
00004  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Lesser General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Lesser General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU Lesser General Public
00017  *  License along with this library; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  */
00021 
00022 #include "value.h"
00023 #include "object.h"
00024 #include "types.h"
00025 #include "interpreter.h"
00026 #include "operations.h"
00027 #include "array_object.h"
00028 #include "internal.h"
00029 #include "error_object.h"
00030 
00031 #include "array_object.lut.h"
00032 
00033 #include <stdio.h>
00034 #include <assert.h>
00035 
00036 using namespace KJS;
00037 
00038 // ------------------------------ ArrayInstanceImp -----------------------------
00039 
00040 const ClassInfo ArrayInstanceImp::info = {"Array", 0, 0, 0};
00041 
00042 ArrayInstanceImp::ArrayInstanceImp(const Object &proto)
00043   : ObjectImp(proto)
00044 {
00045 }
00046 
00047 // Special implementation of [[Put]] - see ECMA 15.4.5.1
00048 void ArrayInstanceImp::put(ExecState *exec, const UString &propertyName, const Value &value, int attr)
00049 {
00050   if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName))
00051     return;
00052 
00053   if (hasProperty(exec,propertyName)) {
00054     if (propertyName == "length") {
00055       Value len = get(exec,"length");
00056       unsigned int oldLen = len.toUInt32(exec);
00057       unsigned int newLen = value.toUInt32(exec);
00058       // shrink array
00059       for (unsigned int u = newLen; u < oldLen; u++) {
00060     UString p = UString::from(u);
00061     if (hasOwnProperty(exec, p))
00062       deleteProperty(exec, p);
00063       }
00064       ObjectImp::put(exec, "length", Number(newLen), DontEnum | DontDelete);
00065       return;
00066     }
00067     //    put(p, v);
00068   } //  } else
00069     ObjectImp::put(exec, propertyName, value, attr);
00070 
00071   // array index ?
00072   unsigned int idx;
00073   if (!sscanf(propertyName.cstring().c_str(), "%u", &idx)) /* TODO */
00074     return;
00075 
00076   // do we need to update/create the length property ?
00077   if (hasOwnProperty(exec, "length")) {
00078     Value len = get(exec, "length");
00079     if (idx < len.toUInt32(exec))
00080       return;
00081   }
00082 
00083   ObjectImp::put(exec, "length", Number(idx+1), DontDelete | DontEnum);
00084 }
00085 
00086 void ArrayInstanceImp::putDirect(ExecState *exec, const UString &propertyName, const Value &value, int attr)
00087 {
00088   ObjectImp::put(exec,propertyName,value,attr);
00089 }
00090 
00091 bool ArrayInstanceImp::hasOwnProperty(ExecState *exec,
00092                                       const UString &propertyName)
00093 {
00094   // disable this object's prototype temporarily for the hasProperty() call
00095   Value protoBackup = prototype();
00096   setPrototype(Undefined());
00097   bool b = hasProperty(exec, propertyName);
00098   setPrototype(protoBackup);
00099   return b;
00100 }
00101 
00102 // ------------------------------ ArrayPrototypeImp ----------------------------
00103 
00104 const ClassInfo ArrayPrototypeImp::info = {"Array", &ArrayInstanceImp::info, &arrayTable, 0};
00105 
00106 /* Source for array_object.lut.h
00107 @begin arrayTable 13
00108   toString       ArrayProtoFuncImp::ToString       DontEnum|Function 0
00109   toLocaleString ArrayProtoFuncImp::ToLocaleString DontEnum|Function 0
00110   concat         ArrayProtoFuncImp::Concat         DontEnum|Function 0
00111   join           ArrayProtoFuncImp::Join           DontEnum|Function 1
00112   pop            ArrayProtoFuncImp::Pop            DontEnum|Function 0
00113   push           ArrayProtoFuncImp::Push           DontEnum|Function 1
00114   reverse        ArrayProtoFuncImp::Reverse        DontEnum|Function 0
00115   shift          ArrayProtoFuncImp::Shift          DontEnum|Function 0
00116   slice          ArrayProtoFuncImp::Slice          DontEnum|Function 2
00117   sort           ArrayProtoFuncImp::Sort           DontEnum|Function 1
00118   splice         ArrayProtoFuncImp::Splice         DontEnum|Function 2
00119   unshift        ArrayProtoFuncImp::UnShift        DontEnum|Function 1
00120 @end
00121 */
00122 
00123 // ECMA 15.4.4
00124 ArrayPrototypeImp::ArrayPrototypeImp(ExecState *exec,
00125                                      ObjectPrototypeImp *objProto)
00126   : ArrayInstanceImp(Object(objProto))
00127 {
00128   Value protect(this);
00129   setInternalValue(Null());
00130 
00131   // The constructor will be added later, by InterpreterImp, once ArrayObjectImp has been constructed.
00132   put(exec,"length", Number(0), DontEnum | DontDelete);
00133 }
00134 
00135 Value ArrayPrototypeImp::get(ExecState *exec, const UString &propertyName) const
00136 {
00137   //fprintf( stderr, "ArrayPrototypeImp::get(%s)\n", propertyName.ascii() );
00138   return lookupGetFunction<ArrayProtoFuncImp, ArrayInstanceImp>( exec, propertyName, &arrayTable, this );
00139 }
00140 
00141 // ------------------------------ ArrayProtoFuncImp ----------------------------
00142 
00143 ArrayProtoFuncImp::ArrayProtoFuncImp(ExecState *exec, int i, int len)
00144   : InternalFunctionImp(
00145     static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp())
00146     ), id(i)
00147 {
00148   Value protect(this);
00149   put(exec,"length",Number(len),DontDelete|ReadOnly|DontEnum);
00150 }
00151 
00152 bool ArrayProtoFuncImp::implementsCall() const
00153 {
00154   return true;
00155 }
00156 
00157 // ECMA 15.4.4
00158 Value ArrayProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
00159 {
00160   unsigned int length = thisObj.get(exec,"length").toUInt32(exec);
00161 
00162   Value result;
00163   switch (id) {
00164   case ToLocaleString:
00165     // TODO  - see 15.4.4.3
00166     // fall through
00167   case ToString:
00168 
00169     if (!thisObj.inherits(&ArrayInstanceImp::info)) {
00170       Object err = Error::create(exec,TypeError);
00171       exec->setException(err);
00172       return err;
00173     }
00174 
00175     // fall through
00176 
00177   case Join: {
00178     UString separator = ",";
00179     UString str = "";
00180 
00181     if (args.size() > 0)
00182       separator = args[0].toString(exec);
00183     for (unsigned int k = 0; k < length; k++) {
00184       if (k >= 1)
00185         str += separator;
00186       Value element = thisObj.get(exec,UString::from(k));
00187       if (element.type() != UndefinedType && element.type() != NullType)
00188         str += element.toString(exec);
00189     }
00190     result = String(str);
00191     break;
00192   }
00193   case Concat: {
00194     Object arr = Object::dynamicCast(exec->interpreter()->builtinArray().construct(exec,List::empty()));
00195     int n = 0;
00196     Value curArg = thisObj;
00197     Object curObj = Object::dynamicCast(thisObj);
00198     bool first = true;
00199     ListIterator it = args.begin();
00200     for (;;) {
00201       if (curArg.type() == ObjectType &&
00202           curObj.inherits(&ArrayInstanceImp::info)) {
00203         unsigned int k = 0;
00204         if (!first)
00205           length = curObj.get(exec,"length").toUInt32(exec);
00206         while (k < length) {
00207           UString p = UString::from(k);
00208           if (curObj.hasProperty(exec,p))
00209             arr.put(exec,UString::from(n), curObj.get(exec,p));
00210           n++;
00211           k++;
00212         }
00213       } else {
00214         arr.put(exec,UString::from(n), curArg);
00215         n++;
00216       }
00217       if (it == args.end())
00218         break;
00219       curArg = *it;
00220       curObj = Object::dynamicCast(it++); // may be 0
00221       first = false;
00222     }
00223     arr.put(exec,"length", Number(n), DontEnum | DontDelete);
00224 
00225     result = arr;
00226     break;
00227   }
00228   case Pop:{
00229 
00230     if (length == 0) {
00231       thisObj.put(exec, "length", Number(length), DontEnum | DontDelete);
00232       result = Undefined();
00233     } else {
00234       UString str = UString::from(length - 1);
00235       result = thisObj.get(exec,str);
00236       thisObj.deleteProperty(exec, str);
00237       thisObj.put(exec, "length", Number(length - 1), DontEnum | DontDelete);
00238     }
00239     break;
00240   }
00241   case Push: {
00242     for (int n = 0; n < args.size(); n++)
00243       thisObj.put(exec,UString::from(length + n), args[n]);
00244     length += args.size();
00245     thisObj.put(exec,"length", Number(length), DontEnum | DontDelete);
00246     result = Number(length);
00247     break;
00248   }
00249   case Reverse: {
00250 
00251     unsigned int middle = length / 2;
00252 
00253     for (unsigned int k = 0; k < middle; k++) {
00254       UString str = UString::from(k);
00255       UString str2 = UString::from(length - k - 1);
00256       Value obj = thisObj.get(exec,str);
00257       Value obj2 = thisObj.get(exec,str2);
00258       if (thisObj.hasProperty(exec,str2)) {
00259         if (thisObj.hasProperty(exec,str)) {
00260           thisObj.put(exec, str, obj2);
00261           thisObj.put(exec, str2, obj);
00262         } else {
00263           thisObj.put(exec, str, obj2);
00264           thisObj.deleteProperty(exec, str2);
00265         }
00266       } else {
00267         if (thisObj.hasProperty(exec, str)) {
00268           thisObj.deleteProperty(exec, str);
00269           thisObj.put(exec, str2, obj);
00270         } else {
00271           // why delete something that's not there ? Strange.
00272           thisObj.deleteProperty(exec, str);
00273           thisObj.deleteProperty(exec, str2);
00274         }
00275       }
00276     }
00277     result = thisObj;
00278     break;
00279   }
00280   case Shift: {
00281     if (length == 0) {
00282       thisObj.put(exec, "length", Number(length), DontEnum | DontDelete);
00283       result = Undefined();
00284     } else {
00285       result = thisObj.get(exec, "0");
00286       for(unsigned int k = 1; k < length; k++) {
00287         UString str = UString::from(k);
00288         UString str2 = UString::from(k-1);
00289         if (thisObj.hasProperty(exec, str)) {
00290           Value obj = thisObj.get(exec, str);
00291           thisObj.put(exec, str2, obj);
00292         } else
00293           thisObj.deleteProperty(exec, str2);
00294       }
00295       thisObj.deleteProperty(exec, UString::from(length - 1));
00296       thisObj.put(exec, "length", Number(length - 1), DontEnum | DontDelete);
00297     }
00298     break;
00299   }
00300   case Slice: {
00301     // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
00302 
00303     // We return a new array
00304     Object resObj = Object::dynamicCast(exec->interpreter()->builtinArray().construct(exec,List::empty()));
00305     result = resObj;
00306     int begin = args[0].toUInt32(exec);
00307     if ( begin < 0 )
00308       begin = maxInt( begin + length, 0 );
00309     else
00310       begin = minInt( begin, length );
00311     int end = length;
00312     if (args[1].type() != UndefinedType)
00313     {
00314       end = args[1].toUInt32(exec);
00315       if ( end < 0 )
00316         end = maxInt( end + length, 0 );
00317       else
00318         end = minInt( end, length );
00319     }
00320 
00321     //printf( "Slicing from %d to %d \n", begin, end );
00322     for(unsigned int k = 0; k < (unsigned int) end-begin; k++) {
00323       UString str = UString::from(k+begin);
00324       if (thisObj.hasProperty(exec,str)) {
00325         UString str2 = UString::from(k);
00326         Value obj = thisObj.get(exec, str);
00327         resObj.put(exec, str2, obj);
00328       }
00329     }
00330     resObj.put(exec, "length", Number(end - begin), DontEnum | DontDelete);
00331     break;
00332   }
00333   case Sort:{
00334 #if 0
00335     printf("KJS Array::Sort length=%d\n", length);
00336     for ( unsigned int i = 0 ; i<length ; ++i )
00337       printf("KJS Array::Sort: %d: %s\n", i, thisObj.get(UString::from(i)).toString().value().ascii() );
00338 #endif
00339     Object sortFunction;
00340     bool useSortFunction = (args[0].type() != UndefinedType);
00341     if (useSortFunction)
00342       {
00343         sortFunction = args[0].toObject(exec);
00344         if (!sortFunction.implementsCall())
00345           useSortFunction = false;
00346       }
00347 
00348     if (length == 0) {
00349       thisObj.put(exec, "length", Number(0), DontEnum | DontDelete);
00350       result = Undefined();
00351       break;
00352     }
00353 
00354     // "Min" sort. Not the fastest, but definitely less code than heapsort
00355     // or quicksort, and much less swapping than bubblesort/insertionsort.
00356     for ( unsigned int i = 0 ; i<length-1 ; ++i )
00357       {
00358         Value iObj = thisObj.get(exec,UString::from(i));
00359         unsigned int themin = i;
00360         Value minObj = iObj;
00361         for ( unsigned int j = i+1 ; j<length ; ++j )
00362           {
00363             Value jObj = thisObj.get(exec,UString::from(j));
00364             int cmp;
00365             if (jObj.type() == UndefinedType) {
00366               cmp = 1;
00367             } else if (minObj.type() == UndefinedType) {
00368               cmp = -1;
00369             } else if (useSortFunction) {
00370                 List l;
00371                 l.append(jObj);
00372                 l.append(minObj);
00373                 Object thisObj = exec->interpreter()->globalObject();
00374                 cmp = sortFunction.call(exec,thisObj, l ).toInt32(exec);
00375             } else {
00376               cmp = (jObj.toString(exec) < minObj.toString(exec)) ? -1 : 1;
00377             }
00378             if ( cmp < 0 )
00379               {
00380                 themin = j;
00381                 minObj = jObj;
00382               }
00383           }
00384         // Swap themin and i
00385         if ( themin > i )
00386           {
00387             //printf("KJS Array::Sort: swapping %d and %d\n", i, themin );
00388             thisObj.put( exec, UString::from(i), minObj );
00389             thisObj.put( exec, UString::from(themin), iObj );
00390           }
00391       }
00392 #if 0
00393     printf("KJS Array::Sort -- Resulting array:\n");
00394     for ( unsigned int i = 0 ; i<length ; ++i )
00395       printf("KJS Array::Sort: %d: %s\n", i, thisObj.get(UString::from(i)).toString().value().ascii() );
00396 #endif
00397     result = thisObj;
00398     break;
00399   }
00400   case Splice: {
00401     // 15.4.4.12 - oh boy this is huge
00402     Object resObj = Object::dynamicCast(exec->interpreter()->builtinArray().construct(exec,List::empty()));
00403     result = resObj;
00404     int begin = args[0].toUInt32(exec);
00405     if ( begin < 0 )
00406       begin = maxInt( begin + length, 0 );
00407     else
00408       begin = minInt( begin, length );
00409     unsigned int deleteCount = minInt( maxInt( args[1].toUInt32(exec), 0 ), length - begin );
00410 
00411     //printf( "Splicing from %d, deleteCount=%d \n", begin, deleteCount );
00412     for(unsigned int k = 0; k < deleteCount; k++) {
00413       UString str = UString::from(k+begin);
00414       if (thisObj.hasProperty(exec,str)) {
00415         UString str2 = UString::from(k);
00416         Value obj = thisObj.get(exec, str);
00417         resObj.put(exec, str2, obj);
00418       }
00419     }
00420     resObj.put(exec, "length", Number(deleteCount), DontEnum | DontDelete);
00421 
00422     unsigned int additionalArgs = maxInt( args.size() - 2, 0 );
00423     if ( additionalArgs != deleteCount )
00424     {
00425       if ( additionalArgs < deleteCount )
00426       {
00427         for ( unsigned int k = begin; k < length - deleteCount; ++k )
00428         {
00429           UString str = UString::from(k+deleteCount);
00430           UString str2 = UString::from(k+additionalArgs);
00431           if (thisObj.hasProperty(exec,str)) {
00432             Value obj = thisObj.get(exec, str);
00433             thisObj.put(exec, str2, obj);
00434           }
00435           else
00436             thisObj.deleteProperty(exec, str2);
00437         }
00438         for ( unsigned int k = length ; k > length - deleteCount + additionalArgs; --k )
00439           thisObj.deleteProperty(exec, UString::from(k-1));
00440       }
00441       else
00442       {
00443         for ( unsigned int k = length - deleteCount; (int)k > begin; --k )
00444         {
00445           UString str = UString::from(k+deleteCount-1);
00446           UString str2 = UString::from(k+additionalArgs-1);
00447           if (thisObj.hasProperty(exec,str)) {
00448             Value obj = thisObj.get(exec, str);
00449             thisObj.put(exec, str2, obj);
00450           }
00451           else
00452             thisObj.deleteProperty(exec, str2);
00453         }
00454       }
00455     }
00456     for ( unsigned int k = 0; k < additionalArgs; ++k )
00457     {
00458       thisObj.put(exec, UString::from(k+begin), args[k+2]);
00459     }
00460     thisObj.put(exec, "length", Number(length - deleteCount + additionalArgs), DontEnum | DontDelete);
00461     break;
00462   }
00463   case UnShift: { // 15.4.4.13
00464     unsigned int nrArgs = args.size();
00465     for ( unsigned int k = length; k > 0; --k )
00466     {
00467       UString str = UString::from(k-1);
00468       UString str2 = UString::from(k+nrArgs-1);
00469       if (thisObj.hasProperty(exec,str)) {
00470         Value obj = thisObj.get(exec, str);
00471         thisObj.put(exec, str2, obj);
00472       } else {
00473         thisObj.deleteProperty(exec, str2);
00474       }
00475     }
00476     for ( unsigned int k = 0; k < nrArgs; ++k )
00477       thisObj.put(exec, UString::from(k), args[k]);
00478     result = Number(length + nrArgs);
00479     thisObj.put(exec, "length", result, DontEnum | DontDelete);
00480     break;
00481   }
00482   default:
00483     assert(0);
00484     break;
00485   }
00486   return result;
00487 }
00488 
00489 // ------------------------------ ArrayObjectImp -------------------------------
00490 
00491 ArrayObjectImp::ArrayObjectImp(ExecState *exec,
00492                                FunctionPrototypeImp *funcProto,
00493                                ArrayPrototypeImp *arrayProto)
00494   : InternalFunctionImp(funcProto)
00495 {
00496   Value protect(this);
00497   // ECMA 15.4.3.1 Array.prototype
00498   put(exec,"prototype", Object(arrayProto), DontEnum|DontDelete|ReadOnly);
00499 
00500   // no. of arguments for constructor
00501   put(exec,"length", Number(1), ReadOnly|DontDelete|DontEnum);
00502 }
00503 
00504 bool ArrayObjectImp::implementsConstruct() const
00505 {
00506   return true;
00507 }
00508 
00509 // ECMA 15.4.2
00510 Object ArrayObjectImp::construct(ExecState *exec, const List &args)
00511 {
00512   Object result(new ArrayInstanceImp(exec->interpreter()->builtinArrayPrototype()));
00513 
00514   unsigned int len;
00515   ListIterator it = args.begin();
00516   // a single argument might denote the array size
00517   if (args.size() == 1 && it->type() == NumberType)
00518     len = it->toUInt32(exec);
00519   else {
00520     // initialize array
00521     len = args.size();
00522     for (unsigned int u = 0; it != args.end(); it++, u++)
00523       result.put(exec, UString::from(u), *it);
00524   }
00525 
00526   // array size
00527   result.put(exec, "length", Number(len), DontEnum | DontDelete);
00528   static_cast<ArrayInstanceImp*>(result.imp())->putDirect(exec, "length", Number(len), DontEnum | DontDelete);
00529 
00530   return result;
00531 }
00532 
00533 bool ArrayObjectImp::implementsCall() const
00534 {
00535   return true;
00536 }
00537 
00538 // ECMA 15.6.1
00539 Value ArrayObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
00540 {
00541   // equivalent to 'new Array(....)'
00542   return construct(exec,args);
00543 }
00544 
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:15:17 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001