array_object.cpp Source File (kjs Library)

| # kjs Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# 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

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:08:15 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001