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 * Copyright (C) 2003 Apple Computer, Inc. 00006 * 00007 * This library is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2 of the License, or (at your option) any later version. 00011 * 00012 * This library is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with this library; if not, write to the Free Software 00019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00020 * 00021 */ 00022 00023 #include "value.h" 00024 #include "object.h" 00025 #include "types.h" 00026 #include "interpreter.h" 00027 #include "operations.h" 00028 #include "array_object.h" 00029 #include "internal.h" 00030 #include "error_object.h" 00031 00032 #include "array_object.lut.h" 00033 00034 #include <stdio.h> 00035 #include <string.h> 00036 #include <assert.h> 00037 00038 #define MAX_INDEX 4294967294U // 2^32-2 00039 00040 using namespace KJS; 00041 00042 // ------------------------------ ArrayInstanceImp ----------------------------- 00043 00044 const unsigned sparseArrayCutoff = 10000; 00045 00046 const ClassInfo ArrayInstanceImp::info = {"Array", 0, 0, 0}; 00047 00048 ArrayInstanceImp::ArrayInstanceImp(ObjectImp *proto, unsigned initialLength) 00049 : ObjectImp(proto) 00050 , length(initialLength) 00051 , storageLength(initialLength < sparseArrayCutoff ? initialLength : 0) 00052 , capacity(storageLength) 00053 , storage(capacity ? (ValueImp **)calloc(capacity, sizeof(ValueImp *)) : 0) 00054 { 00055 } 00056 00057 ArrayInstanceImp::ArrayInstanceImp(ObjectImp *proto, const List &list) 00058 : ObjectImp(proto) 00059 , length(list.size()) 00060 , storageLength(length) 00061 , capacity(storageLength) 00062 , storage(capacity ? (ValueImp **)malloc(sizeof(ValueImp *) * capacity) : 0) 00063 { 00064 ListIterator it = list.begin(); 00065 unsigned l = length; 00066 for (unsigned i = 0; i < l; ++i) { 00067 storage[i] = (it++).imp(); 00068 } 00069 } 00070 00071 ArrayInstanceImp::~ArrayInstanceImp() 00072 { 00073 free(storage); 00074 } 00075 00076 Value ArrayInstanceImp::get(ExecState *exec, const Identifier &propertyName) const 00077 { 00078 if (propertyName == lengthPropertyName) 00079 return Number(length); 00080 00081 bool ok; 00082 unsigned index = propertyName.toArrayIndex(&ok); 00083 if (ok) { 00084 if (index >= length) 00085 return Undefined(); 00086 if (index < storageLength) { 00087 ValueImp *v = storage[index]; 00088 return v ? Value(v) : Undefined(); 00089 } 00090 } 00091 00092 return ObjectImp::get(exec, propertyName); 00093 } 00094 00095 Value ArrayInstanceImp::getPropertyByIndex(ExecState *exec, 00096 unsigned index) const 00097 { 00098 if (index > MAX_INDEX) 00099 return ObjectImp::get(exec, Identifier::from(index)); 00100 if (index >= length) 00101 return Undefined(); 00102 if (index < storageLength) { 00103 ValueImp *v = storage[index]; 00104 return v ? Value(v) : Undefined(); 00105 } 00106 00107 return ObjectImp::get(exec, Identifier::from(index)); 00108 } 00109 00110 // Special implementation of [[Put]] - see ECMA 15.4.5.1 00111 void ArrayInstanceImp::put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr) 00112 { 00113 if (propertyName == lengthPropertyName) { 00114 unsigned int newLen = value.toUInt32(exec); 00115 if (value.toNumber(exec) != double(newLen)) { 00116 Object err = Error::create(exec, RangeError, "Invalid array length."); 00117 exec->setException(err); 00118 return; 00119 } 00120 setLength(newLen, exec); 00121 return; 00122 } 00123 00124 bool ok; 00125 unsigned index = propertyName.toArrayIndex(&ok); 00126 if (ok) { 00127 putPropertyByIndex(exec, index, value, attr); 00128 return; 00129 } 00130 00131 ObjectImp::put(exec, propertyName, value, attr); 00132 } 00133 00134 void ArrayInstanceImp::putPropertyByIndex(ExecState *exec, unsigned index, 00135 const Value &value, int attr) 00136 { 00137 if (index < sparseArrayCutoff && index >= storageLength) { 00138 resizeStorage(index + 1); 00139 } 00140 00141 if (index >= length && index <= MAX_INDEX) { 00142 length = index + 1; 00143 } 00144 00145 if (index < storageLength) { 00146 storage[index] = value.imp(); 00147 return; 00148 } 00149 00150 assert(index >= sparseArrayCutoff); 00151 ObjectImp::put(exec, Identifier::from(index), value, attr); 00152 } 00153 00154 bool ArrayInstanceImp::hasProperty(ExecState *exec, const Identifier &propertyName) const 00155 { 00156 if (propertyName == lengthPropertyName) 00157 return true; 00158 00159 bool ok; 00160 unsigned index = propertyName.toArrayIndex(&ok); 00161 if (ok) { 00162 if (index >= length) 00163 return false; 00164 if (index < storageLength) { 00165 ValueImp *v = storage[index]; 00166 return v && v != UndefinedImp::staticUndefined; 00167 } 00168 } 00169 00170 return ObjectImp::hasProperty(exec, propertyName); 00171 } 00172 00173 bool ArrayInstanceImp::hasPropertyByIndex(ExecState *exec, unsigned index) const 00174 { 00175 if (index > MAX_INDEX) 00176 return ObjectImp::hasProperty(exec, Identifier::from(index)); 00177 if (index >= length) 00178 return false; 00179 if (index < storageLength) { 00180 ValueImp *v = storage[index]; 00181 return v && v != UndefinedImp::staticUndefined; 00182 } 00183 00184 return ObjectImp::hasProperty(exec, Identifier::from(index)); 00185 } 00186 00187 bool ArrayInstanceImp::deleteProperty(ExecState *exec, const Identifier &propertyName) 00188 { 00189 if (propertyName == lengthPropertyName) 00190 return false; 00191 00192 bool ok; 00193 unsigned index = propertyName.toArrayIndex(&ok); 00194 if (ok) { 00195 if (index >= length) 00196 return true; 00197 if (index < storageLength) { 00198 storage[index] = 0; 00199 return true; 00200 } 00201 } 00202 00203 return ObjectImp::deleteProperty(exec, propertyName); 00204 } 00205 00206 bool ArrayInstanceImp::deletePropertyByIndex(ExecState *exec, unsigned index) 00207 { 00208 if (index > MAX_INDEX) 00209 return ObjectImp::deleteProperty(exec, Identifier::from(index)); 00210 if (index >= length) 00211 return true; 00212 if (index < storageLength) { 00213 storage[index] = 0; 00214 return true; 00215 } 00216 00217 return ObjectImp::deleteProperty(exec, Identifier::from(index)); 00218 } 00219 00220 ReferenceList ArrayInstanceImp::propList(ExecState *exec, bool recursive) 00221 { 00222 ReferenceList properties = ObjectImp::propList(exec,recursive); 00223 00224 // avoid fetching this every time through the loop 00225 ValueImp *undefined = UndefinedImp::staticUndefined; 00226 00227 for (unsigned i = 0; i < storageLength; ++i) { 00228 ValueImp *imp = storage[i]; 00229 if (imp && imp != undefined && !ObjectImp::hasProperty(exec,Identifier::from(i))) { 00230 properties.append(Reference(this, i)); 00231 } 00232 } 00233 return properties; 00234 } 00235 00236 void ArrayInstanceImp::resizeStorage(unsigned newLength) 00237 { 00238 if (newLength < storageLength) { 00239 memset(storage + newLength, 0, sizeof(ValueImp *) * (storageLength - newLength)); 00240 } 00241 if (newLength > capacity) { 00242 unsigned newCapacity; 00243 if (newLength > sparseArrayCutoff) { 00244 newCapacity = newLength; 00245 } else { 00246 newCapacity = (newLength * 3 + 1) / 2; 00247 if (newCapacity > sparseArrayCutoff) { 00248 newCapacity = sparseArrayCutoff; 00249 } 00250 } 00251 storage = (ValueImp **)realloc(storage, newCapacity * sizeof (ValueImp *)); 00252 memset(storage + capacity, 0, sizeof(ValueImp *) * (newCapacity - capacity)); 00253 capacity = newCapacity; 00254 } 00255 storageLength = newLength; 00256 } 00257 00258 void ArrayInstanceImp::setLength(unsigned newLength, ExecState *exec) 00259 { 00260 if (newLength <= storageLength) { 00261 resizeStorage(newLength); 00262 } 00263 00264 if (newLength < length) { 00265 ReferenceList sparseProperties; 00266 00267 _prop.addSparseArrayPropertiesToReferenceList(sparseProperties, Object(this)); 00268 00269 ReferenceListIterator it = sparseProperties.begin(); 00270 while (it != sparseProperties.end()) { 00271 Reference ref = it++; 00272 bool ok; 00273 unsigned index = ref.getPropertyName(exec).toArrayIndex(&ok); 00274 if (ok && index > newLength) { 00275 ref.deleteValue(exec); 00276 } 00277 } 00278 } 00279 00280 length = newLength; 00281 } 00282 00283 void ArrayInstanceImp::mark() 00284 { 00285 ObjectImp::mark(); 00286 unsigned l = storageLength; 00287 for (unsigned i = 0; i < l; ++i) { 00288 ValueImp *imp = storage[i]; 00289 if (imp && !imp->marked()) 00290 imp->mark(); 00291 } 00292 } 00293 00294 static ExecState *execForCompareByStringForQSort; 00295 00296 static int compareByStringForQSort(const void *a, const void *b) 00297 { 00298 ExecState *exec = execForCompareByStringForQSort; 00299 ValueImp *va = *(ValueImp **)a; 00300 ValueImp *vb = *(ValueImp **)b; 00301 if (va->dispatchType() == UndefinedType) { 00302 return vb->dispatchType() == UndefinedType ? 0 : 1; 00303 } 00304 if (vb->dispatchType() == UndefinedType) { 00305 return -1; 00306 } 00307 return compare(va->dispatchToString(exec), vb->dispatchToString(exec)); 00308 } 00309 00310 void ArrayInstanceImp::sort(ExecState *exec) 00311 { 00312 int lengthNotIncludingUndefined = pushUndefinedObjectsToEnd(exec); 00313 00314 execForCompareByStringForQSort = exec; 00315 qsort(storage, lengthNotIncludingUndefined, sizeof(ValueImp *), compareByStringForQSort); 00316 execForCompareByStringForQSort = 0; 00317 } 00318 00319 struct CompareWithCompareFunctionArguments { 00320 CompareWithCompareFunctionArguments(ExecState *e, ObjectImp *cf) 00321 : exec(e) 00322 , compareFunction(cf) 00323 , globalObject(e->interpreter()->globalObject()) 00324 { 00325 arguments.append(Undefined()); 00326 arguments.append(Undefined()); 00327 } 00328 00329 ExecState *exec; 00330 ObjectImp *compareFunction; 00331 List arguments; 00332 Object globalObject; 00333 }; 00334 00335 static CompareWithCompareFunctionArguments *compareWithCompareFunctionArguments; 00336 00337 static int compareWithCompareFunctionForQSort(const void *a, const void *b) 00338 { 00339 CompareWithCompareFunctionArguments *args = compareWithCompareFunctionArguments; 00340 00341 ValueImp *va = *(ValueImp **)a; 00342 ValueImp *vb = *(ValueImp **)b; 00343 if (va->dispatchType() == UndefinedType) { 00344 return vb->dispatchType() == UndefinedType ? 0 : 1; 00345 } 00346 if (vb->dispatchType() == UndefinedType) { 00347 return -1; 00348 } 00349 00350 args->arguments.clear(); 00351 args->arguments.append(va); 00352 args->arguments.append(vb); 00353 double v = args->compareFunction->call(args->exec, args->globalObject, args->arguments) 00354 .toNumber(args->exec); 00355 00356 // v may be outside integer range; check sign 00357 if (v > 0) 00358 return 1; 00359 else if (v < 0) 00360 return -1; 00361 else 00362 return 0; 00363 } 00364 00365 void ArrayInstanceImp::sort(ExecState *exec, Object &compareFunction) 00366 { 00367 int lengthNotIncludingUndefined = pushUndefinedObjectsToEnd(exec); 00368 00369 CompareWithCompareFunctionArguments args(exec, compareFunction.imp()); 00370 compareWithCompareFunctionArguments = &args; 00371 qsort(storage, lengthNotIncludingUndefined, sizeof(ValueImp *), compareWithCompareFunctionForQSort); 00372 compareWithCompareFunctionArguments = 0; 00373 } 00374 00375 unsigned ArrayInstanceImp::pushUndefinedObjectsToEnd(ExecState *exec) 00376 { 00377 ValueImp *undefined = UndefinedImp::staticUndefined; 00378 00379 unsigned o = 0; 00380 00381 for (unsigned i = 0; i != storageLength; ++i) { 00382 ValueImp *v = storage[i]; 00383 if (v && v != undefined) { 00384 if (o != i) 00385 storage[o] = v; 00386 o++; 00387 } 00388 } 00389 00390 ReferenceList sparseProperties; 00391 _prop.addSparseArrayPropertiesToReferenceList(sparseProperties, Object(this)); 00392 unsigned newLength = o + sparseProperties.length(); 00393 00394 if (newLength > storageLength) { 00395 resizeStorage(newLength); 00396 } 00397 00398 ReferenceListIterator it = sparseProperties.begin(); 00399 while (it != sparseProperties.end()) { 00400 Reference ref = it++; 00401 storage[o] = ref.getValue(exec).imp(); 00402 ObjectImp::deleteProperty(exec, ref.getPropertyName(exec)); 00403 o++; 00404 } 00405 00406 if (newLength != storageLength) 00407 memset(storage + o, 0, sizeof(ValueImp *) * (storageLength - o)); 00408 00409 return o; 00410 } 00411 00412 // ------------------------------ ArrayPrototypeImp ---------------------------- 00413 00414 const ClassInfo ArrayPrototypeImp::info = {"Array", &ArrayInstanceImp::info, &arrayTable, 0}; 00415 00416 /* Source for array_object.lut.h 00417 @begin arrayTable 17 00418 toString ArrayProtoFuncImp::ToString DontEnum|Function 0 00419 toLocaleString ArrayProtoFuncImp::ToLocaleString DontEnum|Function 0 00420 concat ArrayProtoFuncImp::Concat DontEnum|Function 0 00421 join ArrayProtoFuncImp::Join DontEnum|Function 1 00422 pop ArrayProtoFuncImp::Pop DontEnum|Function 0 00423 push ArrayProtoFuncImp::Push DontEnum|Function 1 00424 reverse ArrayProtoFuncImp::Reverse DontEnum|Function 0 00425 shift ArrayProtoFuncImp::Shift DontEnum|Function 0 00426 slice ArrayProtoFuncImp::Slice DontEnum|Function 2 00427 sort ArrayProtoFuncImp::Sort DontEnum|Function 1 00428 splice ArrayProtoFuncImp::Splice DontEnum|Function 2 00429 unshift ArrayProtoFuncImp::UnShift DontEnum|Function 1 00430 @end 00431 */ 00432 00433 // ECMA 15.4.4 00434 ArrayPrototypeImp::ArrayPrototypeImp(ExecState */*exec*/, 00435 ObjectPrototypeImp *objProto) 00436 : ArrayInstanceImp(objProto, 0) 00437 { 00438 Value protect(this); 00439 setInternalValue(Null()); 00440 } 00441 00442 Value ArrayPrototypeImp::get(ExecState *exec, const Identifier &propertyName) const 00443 { 00444 //fprintf( stderr, "ArrayPrototypeImp::get(%s)\n", propertyName.ascii() ); 00445 return lookupGetFunction<ArrayProtoFuncImp, ArrayInstanceImp>( exec, propertyName, &arrayTable, this ); 00446 } 00447 00448 // ------------------------------ ArrayProtoFuncImp ---------------------------- 00449 00450 ArrayProtoFuncImp::ArrayProtoFuncImp(ExecState *exec, int i, int len) 00451 : InternalFunctionImp( 00452 static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp()) 00453 ), id(i) 00454 { 00455 Value protect(this); 00456 put(exec,lengthPropertyName,Number(len),DontDelete|ReadOnly|DontEnum); 00457 } 00458 00459 bool ArrayProtoFuncImp::implementsCall() const 00460 { 00461 return true; 00462 } 00463 00464 UString valueToLocaleString(ExecState *exec, Value v) 00465 { 00466 Object o = v.toObject(exec); 00467 Object toLocaleString = Object::dynamicCast(o.get(exec,toLocaleStringPropertyName)); 00468 List args; 00469 if (toLocaleString.isValid() && toLocaleString.implementsCall()) 00470 return toLocaleString.call(exec,o,args).toString(exec); 00471 else 00472 return o.toString(exec); 00473 } 00474 00475 // ECMA 15.4.4 00476 Value ArrayProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args) 00477 { 00478 unsigned int length = thisObj.get(exec,lengthPropertyName).toUInt32(exec); 00479 00480 Value result; 00481 switch (id) { 00482 case ToLocaleString: 00483 // fall through 00484 case ToString: 00485 if (!thisObj.inherits(&ArrayInstanceImp::info)) { 00486 Object err = Error::create(exec,TypeError); 00487 exec->setException(err); 00488 return err; 00489 } 00490 // fall through 00491 case Join: { 00492 UString separator = ","; 00493 UString str = ""; 00494 00495 if (id == Join && args.size() > 0 && !args[0].isA(UndefinedType)) 00496 separator = args[0].toString(exec); 00497 for (unsigned int k = 0; k < length; k++) { 00498 if (k >= 1) 00499 str += separator; 00500 Value element = thisObj.get(exec,k); 00501 if (element.type() != UndefinedType && element.type() != NullType) 00502 str += (id == ToLocaleString ? valueToLocaleString(exec,element) : element.toString(exec)); 00503 if (exec->hadException()) 00504 break; 00505 } 00506 result = String(str); 00507 break; 00508 } 00509 case Concat: { 00510 Object arr = Object::dynamicCast(exec->interpreter()->builtinArray().construct(exec,List::empty())); 00511 int n = 0; 00512 Value curArg = thisObj; 00513 Object curObj = Object::dynamicCast(thisObj); 00514 ListIterator it = args.begin(); 00515 for (;;) { 00516 if (curArg.type() == ObjectType && 00517 curObj.inherits(&ArrayInstanceImp::info)) { 00518 unsigned int k = 0; 00519 // Older versions tried to optimize out getting the length of thisObj 00520 // by checking for n != 0, but that doesn't work if thisObj is an empty array. 00521 length = curObj.get(exec,lengthPropertyName).toUInt32(exec); 00522 while (k < length) { 00523 if (curObj.hasProperty(exec,k)) 00524 arr.put(exec, n, curObj.get(exec, k)); 00525 n++; 00526 k++; 00527 } 00528 } else { 00529 arr.put(exec, n, curArg); 00530 n++; 00531 } 00532 if (it == args.end()) 00533 break; 00534 curArg = *it; 00535 curObj = Object::dynamicCast(it++); // may be 0 00536 } 00537 arr.put(exec,lengthPropertyName, Number(n), DontEnum | DontDelete); 00538 00539 result = arr; 00540 break; 00541 } 00542 case Pop:{ 00543 if (length == 0) { 00544 thisObj.put(exec, lengthPropertyName, Number(length), DontEnum | DontDelete); 00545 result = Undefined(); 00546 } else { 00547 result = thisObj.get(exec, length - 1); 00548 thisObj.put(exec, lengthPropertyName, Number(length - 1), DontEnum | DontDelete); 00549 } 00550 break; 00551 } 00552 case Push: { 00553 for (int n = 0; n < args.size(); n++) 00554 thisObj.put(exec, length + n, args[n]); 00555 length += args.size(); 00556 thisObj.put(exec,lengthPropertyName, Number(length), DontEnum | DontDelete); 00557 result = Number(length); 00558 break; 00559 } 00560 case Reverse: { 00561 00562 unsigned int middle = length / 2; 00563 00564 for (unsigned int k = 0; k < middle; k++) { 00565 unsigned lk1 = length - k - 1; 00566 Value obj = thisObj.get(exec,k); 00567 Value obj2 = thisObj.get(exec,lk1); 00568 if (thisObj.hasProperty(exec,lk1)) { 00569 if (thisObj.hasProperty(exec,k)) { 00570 thisObj.put(exec, k, obj2); 00571 thisObj.put(exec, lk1, obj); 00572 } else { 00573 thisObj.put(exec, k, obj2); 00574 thisObj.deleteProperty(exec, lk1); 00575 } 00576 } else { 00577 if (thisObj.hasProperty(exec, k)) { 00578 thisObj.deleteProperty(exec, k); 00579 thisObj.put(exec, lk1, obj); 00580 } else { 00581 // why delete something that's not there ? Strange. 00582 thisObj.deleteProperty(exec, k); 00583 thisObj.deleteProperty(exec, lk1); 00584 } 00585 } 00586 } 00587 result = thisObj; 00588 break; 00589 } 00590 case Shift: { 00591 if (length == 0) { 00592 thisObj.put(exec, lengthPropertyName, Number(length), DontEnum | DontDelete); 00593 result = Undefined(); 00594 } else { 00595 result = thisObj.get(exec, 0); 00596 for(unsigned int k = 1; k < length; k++) { 00597 if (thisObj.hasProperty(exec, k)) { 00598 Value obj = thisObj.get(exec, k); 00599 thisObj.put(exec, k-1, obj); 00600 } else 00601 thisObj.deleteProperty(exec, k-1); 00602 } 00603 thisObj.deleteProperty(exec, length - 1); 00604 thisObj.put(exec, lengthPropertyName, Number(length - 1), DontEnum | DontDelete); 00605 } 00606 break; 00607 } 00608 case Slice: { 00609 // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10 00610 00611 // We return a new array 00612 Object resObj = Object::dynamicCast(exec->interpreter()->builtinArray().construct(exec,List::empty())); 00613 result = resObj; 00614 int begin = args[0].toInteger(exec); 00615 if ( begin < 0 ) 00616 begin = maxInt( begin + length, 0 ); 00617 else 00618 begin = minInt( begin, length ); 00619 int end = length; 00620 if (args[1].type() != UndefinedType) 00621 { 00622 end = args[1].toInteger(exec); 00623 if ( end < 0 ) 00624 end = maxInt( end + length, 0 ); 00625 else 00626 end = minInt( end, length ); 00627 } 00628 00629 //printf( "Slicing from %d to %d \n", begin, end ); 00630 int n = 0; 00631 for(int k = begin; k < end; k++, n++) { 00632 if (thisObj.hasProperty(exec, k)) { 00633 Value obj = thisObj.get(exec, k); 00634 resObj.put(exec, n, obj); 00635 } 00636 } 00637 resObj.put(exec, lengthPropertyName, Number(n), DontEnum | DontDelete); 00638 break; 00639 } 00640 case Sort:{ 00641 #if 0 00642 printf("KJS Array::Sort length=%d\n", length); 00643 for ( unsigned int i = 0 ; i<length ; ++i ) 00644 printf("KJS Array::Sort: %d: %s\n", i, thisObj.get(exec, i).toString(exec).ascii() ); 00645 #endif 00646 Object sortFunction; 00647 bool useSortFunction = (args[0].type() != UndefinedType); 00648 if (useSortFunction) 00649 { 00650 sortFunction = args[0].toObject(exec); 00651 if (!sortFunction.implementsCall()) 00652 useSortFunction = false; 00653 } 00654 00655 if (thisObj.imp()->classInfo() == &ArrayInstanceImp::info) { 00656 if (useSortFunction) 00657 ((ArrayInstanceImp *)thisObj.imp())->sort(exec, sortFunction); 00658 else 00659 ((ArrayInstanceImp *)thisObj.imp())->sort(exec); 00660 result = thisObj; 00661 break; 00662 } 00663 00664 if (length == 0) { 00665 thisObj.put(exec, lengthPropertyName, Number(0), DontEnum | DontDelete); 00666 result = thisObj; 00667 break; 00668 } 00669 00670 // "Min" sort. Not the fastest, but definitely less code than heapsort 00671 // or quicksort, and much less swapping than bubblesort/insertionsort. 00672 for ( unsigned int i = 0 ; i<length-1 ; ++i ) 00673 { 00674 Value iObj = thisObj.get(exec,i); 00675 unsigned int themin = i; 00676 Value minObj = iObj; 00677 for ( unsigned int j = i+1 ; j<length ; ++j ) 00678 { 00679 Value jObj = thisObj.get(exec,j); 00680 double cmp; 00681 if (jObj.type() == UndefinedType) { 00682 cmp = 1; // don't check minObj because there's no need to differentiate == (0) from > (1) 00683 } else if (minObj.type() == UndefinedType) { 00684 cmp = -1; 00685 } else if (useSortFunction) { 00686 List l; 00687 l.append(jObj); 00688 l.append(minObj); 00689 cmp = sortFunction.call(exec, exec->interpreter()->globalObject(), l).toNumber(exec); 00690 } else { 00691 cmp = (jObj.toString(exec) < minObj.toString(exec)) ? -1 : 1; 00692 } 00693 if ( cmp < 0 ) 00694 { 00695 themin = j; 00696 minObj = jObj; 00697 } 00698 } 00699 // Swap themin and i 00700 if ( themin > i ) 00701 { 00702 //printf("KJS Array::Sort: swapping %d and %d\n", i, themin ); 00703 thisObj.put( exec, i, minObj ); 00704 thisObj.put( exec, themin, iObj ); 00705 } 00706 } 00707 #if 0 00708 printf("KJS Array::Sort -- Resulting array:\n"); 00709 for ( unsigned int i = 0 ; i<length ; ++i ) 00710 printf("KJS Array::Sort: %d: %s\n", i, thisObj.get(exec, i).toString(exec).ascii() ); 00711 #endif 00712 result = thisObj; 00713 break; 00714 } 00715 case Splice: { 00716 // 15.4.4.12 - oh boy this is huge 00717 Object resObj = Object::dynamicCast(exec->interpreter()->builtinArray().construct(exec,List::empty())); 00718 result = resObj; 00719 int begin = args[0].toUInt32(exec); 00720 if ( begin < 0 ) 00721 begin = maxInt( begin + length, 0 ); 00722 else 00723 begin = minInt( begin, length ); 00724 unsigned int deleteCount = minInt( maxInt( args[1].toUInt32(exec), 0 ), length - begin ); 00725 00726 //printf( "Splicing from %d, deleteCount=%d \n", begin, deleteCount ); 00727 for(unsigned int k = 0; k < deleteCount; k++) { 00728 if (thisObj.hasProperty(exec,k+begin)) { 00729 Value obj = thisObj.get(exec, k+begin); 00730 resObj.put(exec, k, obj); 00731 } 00732 } 00733 resObj.put(exec, lengthPropertyName, Number(deleteCount), DontEnum | DontDelete); 00734 00735 unsigned int additionalArgs = maxInt( args.size() - 2, 0 ); 00736 if ( additionalArgs != deleteCount ) 00737 { 00738 if ( additionalArgs < deleteCount ) 00739 { 00740 for ( unsigned int k = begin; k < length - deleteCount; ++k ) 00741 { 00742 if (thisObj.hasProperty(exec,k+deleteCount)) { 00743 Value obj = thisObj.get(exec, k+deleteCount); 00744 thisObj.put(exec, k+additionalArgs, obj); 00745 } 00746 else 00747 thisObj.deleteProperty(exec, k+additionalArgs); 00748 } 00749 for ( unsigned int k = length ; k > length - deleteCount + additionalArgs; --k ) 00750 thisObj.deleteProperty(exec, k-1); 00751 } 00752 else 00753 { 00754 for ( unsigned int k = length - deleteCount; (int)k > begin; --k ) 00755 { 00756 if (thisObj.hasProperty(exec,k+deleteCount-1)) { 00757 Value obj = thisObj.get(exec, k+deleteCount-1); 00758 thisObj.put(exec, k+additionalArgs-1, obj); 00759 } 00760 else 00761 thisObj.deleteProperty(exec, k+additionalArgs-1); 00762 } 00763 } 00764 } 00765 for ( unsigned int k = 0; k < additionalArgs; ++k ) 00766 { 00767 thisObj.put(exec, k+begin, args[k+2]); 00768 } 00769 thisObj.put(exec, lengthPropertyName, Number(length - deleteCount + additionalArgs), DontEnum | DontDelete); 00770 break; 00771 } 00772 case UnShift: { // 15.4.4.13 00773 unsigned int nrArgs = args.size(); 00774 for ( unsigned int k = length; k > 0; --k ) 00775 { 00776 if (thisObj.hasProperty(exec,k-1)) { 00777 Value obj = thisObj.get(exec, k-1); 00778 thisObj.put(exec, k+nrArgs-1, obj); 00779 } else { 00780 thisObj.deleteProperty(exec, k+nrArgs-1); 00781 } 00782 } 00783 for ( unsigned int k = 0; k < nrArgs; ++k ) 00784 thisObj.put(exec, k, args[k]); 00785 result = Number(length + nrArgs); 00786 thisObj.put(exec, lengthPropertyName, result, DontEnum | DontDelete); 00787 break; 00788 } 00789 default: 00790 assert(0); 00791 break; 00792 } 00793 return result; 00794 } 00795 00796 // ------------------------------ ArrayObjectImp ------------------------------- 00797 00798 ArrayObjectImp::ArrayObjectImp(ExecState *exec, 00799 FunctionPrototypeImp *funcProto, 00800 ArrayPrototypeImp *arrayProto) 00801 : InternalFunctionImp(funcProto) 00802 { 00803 Value protect(this); 00804 // ECMA 15.4.3.1 Array.prototype 00805 put(exec,prototypePropertyName, Object(arrayProto), DontEnum|DontDelete|ReadOnly); 00806 00807 // no. of arguments for constructor 00808 put(exec,lengthPropertyName, Number(1), ReadOnly|DontDelete|DontEnum); 00809 } 00810 00811 bool ArrayObjectImp::implementsConstruct() const 00812 { 00813 return true; 00814 } 00815 00816 // ECMA 15.4.2 00817 Object ArrayObjectImp::construct(ExecState *exec, const List &args) 00818 { 00819 // a single numeric argument denotes the array size (!) 00820 if (args.size() == 1 && args[0].type() == NumberType) 00821 return Object(new ArrayInstanceImp(exec->interpreter()->builtinArrayPrototype().imp(), args[0].toUInt32(exec))); 00822 00823 // otherwise the array is constructed with the arguments in it 00824 return Object(new ArrayInstanceImp(exec->interpreter()->builtinArrayPrototype().imp(), args)); 00825 } 00826 00827 bool ArrayObjectImp::implementsCall() const 00828 { 00829 return true; 00830 } 00831 00832 // ECMA 15.6.1 00833 Value ArrayObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args) 00834 { 00835 // equivalent to 'new Array(....)' 00836 return construct(exec,args); 00837 }
KDE Logo
This file is part of the documentation for kjs Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 10 18:55:17 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003