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 namespace KJS { 00320 00321 struct CompareWithCompareFunctionArguments { 00322 CompareWithCompareFunctionArguments(ExecState *e, ObjectImp *cf) 00323 : exec(e) 00324 , compareFunction(cf) 00325 , globalObject(e->interpreter()->globalObject()) 00326 { 00327 arguments.append(Undefined()); 00328 arguments.append(Undefined()); 00329 } 00330 00331 ExecState *exec; 00332 ObjectImp *compareFunction; 00333 List arguments; 00334 Object globalObject; 00335 }; 00336 00337 } 00338 00339 static CompareWithCompareFunctionArguments *compareWithCompareFunctionArguments; 00340 00341 static int compareWithCompareFunctionForQSort(const void *a, const void *b) 00342 { 00343 CompareWithCompareFunctionArguments *args = compareWithCompareFunctionArguments; 00344 00345 ValueImp *va = *(ValueImp **)a; 00346 ValueImp *vb = *(ValueImp **)b; 00347 if (va->dispatchType() == UndefinedType) { 00348 return vb->dispatchType() == UndefinedType ? 0 : 1; 00349 } 00350 if (vb->dispatchType() == UndefinedType) { 00351 return -1; 00352 } 00353 00354 args->arguments.clear(); 00355 args->arguments.append(va); 00356 args->arguments.append(vb); 00357 double v = args->compareFunction->call(args->exec, args->globalObject, args->arguments) 00358 .toNumber(args->exec); 00359 00360 // v may be outside integer range; check sign 00361 if (v > 0) 00362 return 1; 00363 else if (v < 0) 00364 return -1; 00365 else 00366 return 0; 00367 } 00368 00369 void ArrayInstanceImp::sort(ExecState *exec, Object &compareFunction) 00370 { 00371 int lengthNotIncludingUndefined = pushUndefinedObjectsToEnd(exec); 00372 00373 CompareWithCompareFunctionArguments args(exec, compareFunction.imp()); 00374 compareWithCompareFunctionArguments = &args; 00375 qsort(storage, lengthNotIncludingUndefined, sizeof(ValueImp *), compareWithCompareFunctionForQSort); 00376 compareWithCompareFunctionArguments = 0; 00377 } 00378 00379 unsigned ArrayInstanceImp::pushUndefinedObjectsToEnd(ExecState *exec) 00380 { 00381 ValueImp *undefined = UndefinedImp::staticUndefined; 00382 00383 unsigned o = 0; 00384 00385 for (unsigned i = 0; i != storageLength; ++i) { 00386 ValueImp *v = storage[i]; 00387 if (v && v != undefined) { 00388 if (o != i) 00389 storage[o] = v; 00390 o++; 00391 } 00392 } 00393 00394 ReferenceList sparseProperties; 00395 _prop.addSparseArrayPropertiesToReferenceList(sparseProperties, Object(this)); 00396 unsigned newLength = o + sparseProperties.length(); 00397 00398 if (newLength > storageLength) { 00399 resizeStorage(newLength); 00400 } 00401 00402 ReferenceListIterator it = sparseProperties.begin(); 00403 while (it != sparseProperties.end()) { 00404 Reference ref = it++; 00405 storage[o] = ref.getValue(exec).imp(); 00406 ObjectImp::deleteProperty(exec, ref.getPropertyName(exec)); 00407 o++; 00408 } 00409 00410 if (newLength != storageLength) 00411 memset(storage + o, 0, sizeof(ValueImp *) * (storageLength - o)); 00412 00413 return o; 00414 } 00415 00416 // ------------------------------ ArrayPrototypeImp ---------------------------- 00417 00418 const ClassInfo ArrayPrototypeImp::info = {"Array", &ArrayInstanceImp::info, &arrayTable, 0}; 00419 00420 /* Source for array_object.lut.h 00421 @begin arrayTable 17 00422 toString ArrayProtoFuncImp::ToString DontEnum|Function 0 00423 toLocaleString ArrayProtoFuncImp::ToLocaleString DontEnum|Function 0 00424 concat ArrayProtoFuncImp::Concat DontEnum|Function 0 00425 join ArrayProtoFuncImp::Join DontEnum|Function 1 00426 pop ArrayProtoFuncImp::Pop DontEnum|Function 0 00427 push ArrayProtoFuncImp::Push DontEnum|Function 1 00428 reverse ArrayProtoFuncImp::Reverse DontEnum|Function 0 00429 shift ArrayProtoFuncImp::Shift DontEnum|Function 0 00430 slice ArrayProtoFuncImp::Slice DontEnum|Function 2 00431 sort ArrayProtoFuncImp::Sort DontEnum|Function 1 00432 splice ArrayProtoFuncImp::Splice DontEnum|Function 2 00433 unshift ArrayProtoFuncImp::UnShift DontEnum|Function 1 00434 @end 00435 */ 00436 00437 // ECMA 15.4.4 00438 ArrayPrototypeImp::ArrayPrototypeImp(ExecState */*exec*/, 00439 ObjectPrototypeImp *objProto) 00440 : ArrayInstanceImp(objProto, 0) 00441 { 00442 Value protect(this); 00443 setInternalValue(Null()); 00444 } 00445 00446 Value ArrayPrototypeImp::get(ExecState *exec, const Identifier &propertyName) const 00447 { 00448 //fprintf( stderr, "ArrayPrototypeImp::get(%s)\n", propertyName.ascii() ); 00449 return lookupGetFunction<ArrayProtoFuncImp, ArrayInstanceImp>( exec, propertyName, &arrayTable, this ); 00450 } 00451 00452 // ------------------------------ ArrayProtoFuncImp ---------------------------- 00453 00454 ArrayProtoFuncImp::ArrayProtoFuncImp(ExecState *exec, int i, int len) 00455 : InternalFunctionImp( 00456 static_cast<FunctionPrototypeImp*>(exec->interpreter()->builtinFunctionPrototype().imp()) 00457 ), id(i) 00458 { 00459 Value protect(this); 00460 put(exec,lengthPropertyName,Number(len),DontDelete|ReadOnly|DontEnum); 00461 } 00462 00463 bool ArrayProtoFuncImp::implementsCall() const 00464 { 00465 return true; 00466 } 00467 00468 UString valueToLocaleString(ExecState *exec, Value v) 00469 { 00470 Object o = v.toObject(exec); 00471 Object toLocaleString = Object::dynamicCast(o.get(exec,toLocaleStringPropertyName)); 00472 List args; 00473 if (toLocaleString.isValid() && toLocaleString.implementsCall()) 00474 return toLocaleString.call(exec,o,args).toString(exec); 00475 else 00476 return o.toString(exec); 00477 } 00478 00479 // ECMA 15.4.4 00480 Value ArrayProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args) 00481 { 00482 unsigned int length = thisObj.get(exec,lengthPropertyName).toUInt32(exec); 00483 00484 Value result; 00485 switch (id) { 00486 case ToLocaleString: 00487 // fall through 00488 case ToString: 00489 if (!thisObj.inherits(&ArrayInstanceImp::info)) { 00490 Object err = Error::create(exec,TypeError); 00491 exec->setException(err); 00492 return err; 00493 } 00494 // fall through 00495 case Join: { 00496 UString separator = ","; 00497 UString str = ""; 00498 00499 if (id == Join && args.size() > 0 && !args[0].isA(UndefinedType)) 00500 separator = args[0].toString(exec); 00501 for (unsigned int k = 0; k < length; k++) { 00502 if (k >= 1) 00503 str += separator; 00504 Value element = thisObj.get(exec,k); 00505 if (element.type() != UndefinedType && element.type() != NullType) 00506 str += (id == ToLocaleString ? valueToLocaleString(exec,element) : element.toString(exec)); 00507 if (exec->hadException()) 00508 break; 00509 } 00510 result = String(str); 00511 break; 00512 } 00513 case Concat: { 00514 Object arr = Object::dynamicCast(exec->interpreter()->builtinArray().construct(exec,List::empty())); 00515 int n = 0; 00516 Value curArg = thisObj; 00517 Object curObj = Object::dynamicCast(thisObj); 00518 ListIterator it = args.begin(); 00519 for (;;) { 00520 if (curArg.type() == ObjectType && 00521 curObj.inherits(&ArrayInstanceImp::info)) { 00522 unsigned int k = 0; 00523 // Older versions tried to optimize out getting the length of thisObj 00524 // by checking for n != 0, but that doesn't work if thisObj is an empty array. 00525 length = curObj.get(exec,lengthPropertyName).toUInt32(exec); 00526 while (k < length) { 00527 if (curObj.hasProperty(exec,k)) 00528 arr.put(exec, n, curObj.get(exec, k)); 00529 n++; 00530 k++; 00531 } 00532 } else { 00533 arr.put(exec, n, curArg); 00534 n++; 00535 } 00536 if (it == args.end()) 00537 break; 00538 curArg = *it; 00539 curObj = Object::dynamicCast(it++); // may be 0 00540 } 00541 arr.put(exec,lengthPropertyName, Number(n), DontEnum | DontDelete); 00542 00543 result = arr; 00544 break; 00545 } 00546 case Pop:{ 00547 if (length == 0) { 00548 thisObj.put(exec, lengthPropertyName, Number(length), DontEnum | DontDelete); 00549 result = Undefined(); 00550 } else { 00551 result = thisObj.get(exec, length - 1); 00552 thisObj.put(exec, lengthPropertyName, Number(length - 1), DontEnum | DontDelete); 00553 } 00554 break; 00555 } 00556 case Push: { 00557 for (int n = 0; n < args.size(); n++) 00558 thisObj.put(exec, length + n, args[n]); 00559 length += args.size(); 00560 thisObj.put(exec,lengthPropertyName, Number(length), DontEnum | DontDelete); 00561 result = Number(length); 00562 break; 00563 } 00564 case Reverse: { 00565 00566 unsigned int middle = length / 2; 00567 00568 for (unsigned int k = 0; k < middle; k++) { 00569 unsigned lk1 = length - k - 1; 00570 Value obj = thisObj.get(exec,k); 00571 Value obj2 = thisObj.get(exec,lk1); 00572 if (thisObj.hasProperty(exec,lk1)) { 00573 if (thisObj.hasProperty(exec,k)) { 00574 thisObj.put(exec, k, obj2); 00575 thisObj.put(exec, lk1, obj); 00576 } else { 00577 thisObj.put(exec, k, obj2); 00578 thisObj.deleteProperty(exec, lk1); 00579 } 00580 } else { 00581 if (thisObj.hasProperty(exec, k)) { 00582 thisObj.deleteProperty(exec, k); 00583 thisObj.put(exec, lk1, obj); 00584 } else { 00585 // why delete something that's not there ? Strange. 00586 thisObj.deleteProperty(exec, k); 00587 thisObj.deleteProperty(exec, lk1); 00588 } 00589 } 00590 } 00591 result = thisObj; 00592 break; 00593 } 00594 case Shift: { 00595 if (length == 0) { 00596 thisObj.put(exec, lengthPropertyName, Number(length), DontEnum | DontDelete); 00597 result = Undefined(); 00598 } else { 00599 result = thisObj.get(exec, 0); 00600 for(unsigned int k = 1; k < length; k++) { 00601 if (thisObj.hasProperty(exec, k)) { 00602 Value obj = thisObj.get(exec, k); 00603 thisObj.put(exec, k-1, obj); 00604 } else 00605 thisObj.deleteProperty(exec, k-1); 00606 } 00607 thisObj.deleteProperty(exec, length - 1); 00608 thisObj.put(exec, lengthPropertyName, Number(length - 1), DontEnum | DontDelete); 00609 } 00610 break; 00611 } 00612 case Slice: { 00613 // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10 00614 00615 // We return a new array 00616 Object resObj = Object::dynamicCast(exec->interpreter()->builtinArray().construct(exec,List::empty())); 00617 result = resObj; 00618 int begin = 0; 00619 if (args[0].type() != UndefinedType) { 00620 begin = args[0].toInteger(exec); 00621 if ( begin < 0 ) 00622 begin = maxInt( begin + length, 0 ); 00623 else 00624 begin = minInt( begin, length ); 00625 } 00626 int end = length; 00627 if (args[1].type() != UndefinedType) 00628 { 00629 end = args[1].toInteger(exec); 00630 if ( end < 0 ) 00631 end = maxInt( end + length, 0 ); 00632 else 00633 end = minInt( end, length ); 00634 } 00635 00636 //printf( "Slicing from %d to %d \n", begin, end ); 00637 int n = 0; 00638 for(int k = begin; k < end; k++, n++) { 00639 if (thisObj.hasProperty(exec, k)) { 00640 Value obj = thisObj.get(exec, k); 00641 resObj.put(exec, n, obj); 00642 } 00643 } 00644 resObj.put(exec, lengthPropertyName, Number(n), DontEnum | DontDelete); 00645 break; 00646 } 00647 case Sort:{ 00648 #if 0 00649 printf("KJS Array::Sort length=%d\n", length); 00650 for ( unsigned int i = 0 ; i<length ; ++i ) 00651 printf("KJS Array::Sort: %d: %s\n", i, thisObj.get(exec, i).toString(exec).ascii() ); 00652 #endif 00653 Object sortFunction; 00654 bool useSortFunction = (args[0].type() != UndefinedType); 00655 if (useSortFunction) 00656 { 00657 sortFunction = args[0].toObject(exec); 00658 if (!sortFunction.implementsCall()) 00659 useSortFunction = false; 00660 } 00661 00662 if (thisObj.imp()->classInfo() == &ArrayInstanceImp::info) { 00663 if (useSortFunction) 00664 ((ArrayInstanceImp *)thisObj.imp())->sort(exec, sortFunction); 00665 else 00666 ((ArrayInstanceImp *)thisObj.imp())->sort(exec); 00667 result = thisObj; 00668 break; 00669 } 00670 00671 if (length == 0) { 00672 thisObj.put(exec, lengthPropertyName, Number(0), DontEnum | DontDelete); 00673 result = thisObj; 00674 break; 00675 } 00676 00677 // "Min" sort. Not the fastest, but definitely less code than heapsort 00678 // or quicksort, and much less swapping than bubblesort/insertionsort. 00679 for ( unsigned int i = 0 ; i<length-1 ; ++i ) 00680 { 00681 Value iObj = thisObj.get(exec,i); 00682 unsigned int themin = i; 00683 Value minObj = iObj; 00684 for ( unsigned int j = i+1 ; j<length ; ++j ) 00685 { 00686 Value jObj = thisObj.get(exec,j); 00687 double cmp; 00688 if (jObj.type() == UndefinedType) { 00689 cmp = 1; // don't check minObj because there's no need to differentiate == (0) from > (1) 00690 } else if (minObj.type() == UndefinedType) { 00691 cmp = -1; 00692 } else if (useSortFunction) { 00693 List l; 00694 l.append(jObj); 00695 l.append(minObj); 00696 cmp = sortFunction.call(exec, exec->interpreter()->globalObject(), l).toNumber(exec); 00697 } else { 00698 cmp = (jObj.toString(exec) < minObj.toString(exec)) ? -1 : 1; 00699 } 00700 if ( cmp < 0 ) 00701 { 00702 themin = j; 00703 minObj = jObj; 00704 } 00705 } 00706 // Swap themin and i 00707 if ( themin > i ) 00708 { 00709 //printf("KJS Array::Sort: swapping %d and %d\n", i, themin ); 00710 thisObj.put( exec, i, minObj ); 00711 thisObj.put( exec, themin, iObj ); 00712 } 00713 } 00714 #if 0 00715 printf("KJS Array::Sort -- Resulting array:\n"); 00716 for ( unsigned int i = 0 ; i<length ; ++i ) 00717 printf("KJS Array::Sort: %d: %s\n", i, thisObj.get(exec, i).toString(exec).ascii() ); 00718 #endif 00719 result = thisObj; 00720 break; 00721 } 00722 case Splice: { 00723 // 15.4.4.12 - oh boy this is huge 00724 Object resObj = Object::dynamicCast(exec->interpreter()->builtinArray().construct(exec,List::empty())); 00725 result = resObj; 00726 int begin = args[0].toUInt32(exec); 00727 if ( begin < 0 ) 00728 begin = maxInt( begin + length, 0 ); 00729 else 00730 begin = minInt( begin, length ); 00731 unsigned int deleteCount = minInt( maxInt( args[1].toUInt32(exec), 0 ), length - begin ); 00732 00733 //printf( "Splicing from %d, deleteCount=%d \n", begin, deleteCount ); 00734 for(unsigned int k = 0; k < deleteCount; k++) { 00735 if (thisObj.hasProperty(exec,k+begin)) { 00736 Value obj = thisObj.get(exec, k+begin); 00737 resObj.put(exec, k, obj); 00738 } 00739 } 00740 resObj.put(exec, lengthPropertyName, Number(deleteCount), DontEnum | DontDelete); 00741 00742 unsigned int additionalArgs = maxInt( args.size() - 2, 0 ); 00743 if ( additionalArgs != deleteCount ) 00744 { 00745 if ( additionalArgs < deleteCount ) 00746 { 00747 for ( unsigned int k = begin; k < length - deleteCount; ++k ) 00748 { 00749 if (thisObj.hasProperty(exec,k+deleteCount)) { 00750 Value obj = thisObj.get(exec, k+deleteCount); 00751 thisObj.put(exec, k+additionalArgs, obj); 00752 } 00753 else 00754 thisObj.deleteProperty(exec, k+additionalArgs); 00755 } 00756 for ( unsigned int k = length ; k > length - deleteCount + additionalArgs; --k ) 00757 thisObj.deleteProperty(exec, k-1); 00758 } 00759 else 00760 { 00761 for ( unsigned int k = length - deleteCount; (int)k > begin; --k ) 00762 { 00763 if (thisObj.hasProperty(exec,k+deleteCount-1)) { 00764 Value obj = thisObj.get(exec, k+deleteCount-1); 00765 thisObj.put(exec, k+additionalArgs-1, obj); 00766 } 00767 else 00768 thisObj.deleteProperty(exec, k+additionalArgs-1); 00769 } 00770 } 00771 } 00772 for ( unsigned int k = 0; k < additionalArgs; ++k ) 00773 { 00774 thisObj.put(exec, k+begin, args[k+2]); 00775 } 00776 thisObj.put(exec, lengthPropertyName, Number(length - deleteCount + additionalArgs), DontEnum | DontDelete); 00777 break; 00778 } 00779 case UnShift: { // 15.4.4.13 00780 unsigned int nrArgs = args.size(); 00781 for ( unsigned int k = length; k > 0; --k ) 00782 { 00783 if (thisObj.hasProperty(exec,k-1)) { 00784 Value obj = thisObj.get(exec, k-1); 00785 thisObj.put(exec, k+nrArgs-1, obj); 00786 } else { 00787 thisObj.deleteProperty(exec, k+nrArgs-1); 00788 } 00789 } 00790 for ( unsigned int k = 0; k < nrArgs; ++k ) 00791 thisObj.put(exec, k, args[k]); 00792 result = Number(length + nrArgs); 00793 thisObj.put(exec, lengthPropertyName, result, DontEnum | DontDelete); 00794 break; 00795 } 00796 default: 00797 assert(0); 00798 break; 00799 } 00800 return result; 00801 } 00802 00803 // ------------------------------ ArrayObjectImp ------------------------------- 00804 00805 ArrayObjectImp::ArrayObjectImp(ExecState *exec, 00806 FunctionPrototypeImp *funcProto, 00807 ArrayPrototypeImp *arrayProto) 00808 : InternalFunctionImp(funcProto) 00809 { 00810 Value protect(this); 00811 // ECMA 15.4.3.1 Array.prototype 00812 put(exec,prototypePropertyName, Object(arrayProto), DontEnum|DontDelete|ReadOnly); 00813 00814 // no. of arguments for constructor 00815 put(exec,lengthPropertyName, Number(1), ReadOnly|DontDelete|DontEnum); 00816 } 00817 00818 bool ArrayObjectImp::implementsConstruct() const 00819 { 00820 return true; 00821 } 00822 00823 // ECMA 15.4.2 00824 Object ArrayObjectImp::construct(ExecState *exec, const List &args) 00825 { 00826 // a single numeric argument denotes the array size (!) 00827 if (args.size() == 1 && args[0].type() == NumberType) 00828 return Object(new ArrayInstanceImp(exec->interpreter()->builtinArrayPrototype().imp(), args[0].toUInt32(exec))); 00829 00830 // otherwise the array is constructed with the arguments in it 00831 return Object(new ArrayInstanceImp(exec->interpreter()->builtinArrayPrototype().imp(), args)); 00832 } 00833 00834 bool ArrayObjectImp::implementsCall() const 00835 { 00836 return true; 00837 } 00838 00839 // ECMA 15.6.1 00840 Value ArrayObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args) 00841 { 00842 // equivalent to 'new Array(....)' 00843 return construct(exec,args); 00844 }
KDE Logo
This file is part of the documentation for kjs Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 14 00:18:50 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003