]>
Commit | Line | Data |
---|---|---|
b37bf2e1 A |
1 | /* |
2 | * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) | |
3 | * Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved. | |
4 | * Copyright (C) 2003 Peter Kelly (pmk@post.com) | |
5 | * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) | |
6 | * | |
7 | * This library is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2 of the License, or (at your option) any later version. | |
11 | * | |
12 | * This library is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
18 | * License along with this library; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 | |
20 | * USA | |
21 | * | |
22 | */ | |
23 | ||
24 | #include "config.h" | |
25 | #include "array_object.h" | |
26 | #include "array_object.lut.h" | |
27 | ||
28 | #include "error_object.h" | |
29 | #include "lookup.h" | |
30 | #include "operations.h" | |
31 | #include <stdio.h> | |
32 | #include <wtf/Assertions.h> | |
33 | #include <wtf/HashSet.h> | |
34 | ||
35 | #include <algorithm> // for std::min | |
36 | ||
37 | namespace KJS { | |
38 | ||
39 | // ------------------------------ ArrayPrototype ---------------------------- | |
40 | ||
41 | const ClassInfo ArrayPrototype::info = {"Array", &ArrayInstance::info, &arrayTable}; | |
42 | ||
43 | /* Source for array_object.lut.h | |
44 | @begin arrayTable 16 | |
45 | toString arrayProtoFuncToString DontEnum|Function 0 | |
46 | toLocaleString arrayProtoFuncToLocaleString DontEnum|Function 0 | |
47 | concat arrayProtoFuncConcat DontEnum|Function 1 | |
48 | join arrayProtoFuncJoin DontEnum|Function 1 | |
49 | pop arrayProtoFuncPop DontEnum|Function 0 | |
50 | push arrayProtoFuncPush DontEnum|Function 1 | |
51 | reverse arrayProtoFuncReverse DontEnum|Function 0 | |
52 | shift arrayProtoFuncShift DontEnum|Function 0 | |
53 | slice arrayProtoFuncSlice DontEnum|Function 2 | |
54 | sort arrayProtoFuncSort DontEnum|Function 1 | |
55 | splice arrayProtoFuncSplice DontEnum|Function 2 | |
56 | unshift arrayProtoFuncUnShift DontEnum|Function 1 | |
57 | every arrayProtoFuncEvery DontEnum|Function 1 | |
58 | forEach arrayProtoFuncForEach DontEnum|Function 1 | |
59 | some arrayProtoFuncSome DontEnum|Function 1 | |
60 | indexOf arrayProtoFuncIndexOf DontEnum|Function 1 | |
61 | lastIndexOf arrayProtoFuncLastIndexOf DontEnum|Function 1 | |
62 | filter arrayProtoFuncFilter DontEnum|Function 1 | |
63 | map arrayProtoFuncMap DontEnum|Function 1 | |
64 | @end | |
65 | */ | |
66 | ||
67 | // ECMA 15.4.4 | |
68 | ArrayPrototype::ArrayPrototype(ExecState*, ObjectPrototype* objProto) | |
69 | : ArrayInstance(objProto, 0) | |
70 | { | |
71 | } | |
72 | ||
73 | bool ArrayPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) | |
74 | { | |
75 | return getStaticFunctionSlot<ArrayInstance>(exec, &arrayTable, this, propertyName, slot); | |
76 | } | |
77 | ||
78 | ||
79 | // ------------------------------ Array Functions ---------------------------- | |
80 | ||
81 | // Helper function | |
82 | static JSValue* getProperty(ExecState* exec, JSObject* obj, unsigned index) | |
83 | { | |
84 | PropertySlot slot; | |
85 | if (!obj->getPropertySlot(exec, index, slot)) | |
86 | return 0; | |
87 | return slot.getValue(exec, obj, index); | |
88 | } | |
89 | ||
90 | JSValue* arrayProtoFuncToString(ExecState* exec, JSObject* thisObj, const List&) | |
91 | { | |
92 | if (!thisObj->inherits(&ArrayInstance::info)) | |
93 | return throwError(exec, TypeError); | |
94 | ||
95 | static HashSet<JSObject*> visitedElems; | |
96 | static const UString* empty = new UString(""); | |
97 | static const UString* comma = new UString(","); | |
98 | bool alreadyVisited = !visitedElems.add(thisObj).second; | |
99 | if (alreadyVisited) | |
100 | return jsString(*empty); | |
101 | UString separator = *comma; | |
102 | UString str = *empty; | |
103 | ||
104 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
105 | for (unsigned k = 0; k < length; k++) { | |
106 | if (k >= 1) | |
107 | str += separator; | |
108 | if (str.isNull()) { | |
109 | JSObject* error = Error::create(exec, GeneralError, "Out of memory"); | |
110 | exec->setException(error); | |
111 | break; | |
112 | } | |
113 | ||
114 | JSValue* element = thisObj->get(exec, k); | |
115 | if (element->isUndefinedOrNull()) | |
116 | continue; | |
117 | ||
118 | str += element->toString(exec); | |
119 | ||
120 | if (str.isNull()) { | |
121 | JSObject* error = Error::create(exec, GeneralError, "Out of memory"); | |
122 | exec->setException(error); | |
123 | } | |
124 | ||
125 | if (exec->hadException()) | |
126 | break; | |
127 | } | |
128 | visitedElems.remove(thisObj); | |
129 | return jsString(str); | |
130 | } | |
131 | ||
132 | JSValue* arrayProtoFuncToLocaleString(ExecState* exec, JSObject* thisObj, const List&) | |
133 | { | |
134 | if (!thisObj->inherits(&ArrayInstance::info)) | |
135 | return throwError(exec, TypeError); | |
136 | ||
137 | static HashSet<JSObject*> visitedElems; | |
138 | static const UString* empty = new UString(""); | |
139 | static const UString* comma = new UString(","); | |
140 | bool alreadyVisited = !visitedElems.add(thisObj).second; | |
141 | if (alreadyVisited) | |
142 | return jsString(*empty); | |
143 | UString separator = *comma; | |
144 | UString str = *empty; | |
145 | ||
146 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
147 | for (unsigned k = 0; k < length; k++) { | |
148 | if (k >= 1) | |
149 | str += separator; | |
150 | if (str.isNull()) { | |
151 | JSObject* error = Error::create(exec, GeneralError, "Out of memory"); | |
152 | exec->setException(error); | |
153 | break; | |
154 | } | |
155 | ||
156 | JSValue* element = thisObj->get(exec, k); | |
157 | if (element->isUndefinedOrNull()) | |
158 | continue; | |
159 | ||
160 | JSObject* o = element->toObject(exec); | |
161 | JSValue* conversionFunction = o->get(exec, exec->propertyNames().toLocaleString); | |
162 | if (conversionFunction->isObject() && static_cast<JSObject*>(conversionFunction)->implementsCall()) | |
163 | str += static_cast<JSObject*>(conversionFunction)->call(exec, o, exec->emptyList())->toString(exec); | |
164 | else | |
165 | str += element->toString(exec); | |
166 | ||
167 | if (str.isNull()) { | |
168 | JSObject* error = Error::create(exec, GeneralError, "Out of memory"); | |
169 | exec->setException(error); | |
170 | } | |
171 | ||
172 | if (exec->hadException()) | |
173 | break; | |
174 | } | |
175 | visitedElems.remove(thisObj); | |
176 | return jsString(str); | |
177 | } | |
178 | ||
179 | JSValue* arrayProtoFuncJoin(ExecState* exec, JSObject* thisObj, const List& args) | |
180 | { | |
181 | static HashSet<JSObject*> visitedElems; | |
182 | static const UString* empty = new UString(""); | |
183 | static const UString* comma = new UString(","); | |
184 | bool alreadyVisited = !visitedElems.add(thisObj).second; | |
185 | if (alreadyVisited) | |
186 | return jsString(*empty); | |
187 | UString separator = *comma; | |
188 | UString str = *empty; | |
189 | ||
190 | if (!args[0]->isUndefined()) | |
191 | separator = args[0]->toString(exec); | |
192 | ||
193 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
194 | for (unsigned k = 0; k < length; k++) { | |
195 | if (k >= 1) | |
196 | str += separator; | |
197 | if (str.isNull()) { | |
198 | JSObject* error = Error::create(exec, GeneralError, "Out of memory"); | |
199 | exec->setException(error); | |
200 | break; | |
201 | } | |
202 | ||
203 | JSValue* element = thisObj->get(exec, k); | |
204 | if (element->isUndefinedOrNull()) | |
205 | continue; | |
206 | ||
207 | str += element->toString(exec); | |
208 | ||
209 | if (str.isNull()) { | |
210 | JSObject* error = Error::create(exec, GeneralError, "Out of memory"); | |
211 | exec->setException(error); | |
212 | } | |
213 | ||
214 | if (exec->hadException()) | |
215 | break; | |
216 | } | |
217 | visitedElems.remove(thisObj); | |
218 | return jsString(str); | |
219 | } | |
220 | ||
221 | JSValue* arrayProtoFuncConcat(ExecState* exec, JSObject* thisObj, const List& args) | |
222 | { | |
223 | JSObject* arr = static_cast<JSObject*>(exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, exec->emptyList())); | |
224 | int n = 0; | |
225 | JSValue* curArg = thisObj; | |
226 | JSObject* curObj = static_cast<JSObject* >(thisObj); | |
227 | List::const_iterator it = args.begin(); | |
228 | List::const_iterator end = args.end(); | |
229 | while (1) { | |
230 | if (curArg->isObject() && curObj->inherits(&ArrayInstance::info)) { | |
231 | unsigned k = 0; | |
232 | // Older versions tried to optimize out getting the length of thisObj | |
233 | // by checking for n != 0, but that doesn't work if thisObj is an empty array. | |
234 | unsigned length = curObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
235 | while (k < length) { | |
236 | if (JSValue* v = getProperty(exec, curObj, k)) | |
237 | arr->put(exec, n, v); | |
238 | n++; | |
239 | k++; | |
240 | } | |
241 | } else { | |
242 | arr->put(exec, n, curArg); | |
243 | n++; | |
244 | } | |
245 | if (it == end) | |
246 | break; | |
247 | curArg = *it; | |
248 | curObj = static_cast<JSObject*>(curArg); // may be 0 | |
249 | ++it; | |
250 | } | |
251 | arr->put(exec, exec->propertyNames().length, jsNumber(n)); | |
252 | return arr; | |
253 | } | |
254 | ||
255 | JSValue* arrayProtoFuncPop(ExecState* exec, JSObject* thisObj, const List&) | |
256 | { | |
257 | JSValue* result = 0; | |
258 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
259 | if (length == 0) { | |
260 | thisObj->put(exec, exec->propertyNames().length, jsNumber(length)); | |
261 | result = jsUndefined(); | |
262 | } else { | |
263 | result = thisObj->get(exec, length - 1); | |
264 | thisObj->deleteProperty(exec, length - 1); | |
265 | thisObj->put(exec, exec->propertyNames().length, jsNumber(length - 1)); | |
266 | } | |
267 | return result; | |
268 | } | |
269 | ||
270 | JSValue* arrayProtoFuncPush(ExecState* exec, JSObject* thisObj, const List& args) | |
271 | { | |
272 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
273 | for (unsigned n = 0; n < args.size(); n++) | |
274 | thisObj->put(exec, length + n, args[n]); | |
275 | length += args.size(); | |
276 | thisObj->put(exec, exec->propertyNames().length, jsNumber(length)); | |
277 | return jsNumber(length); | |
278 | } | |
279 | ||
280 | JSValue* arrayProtoFuncReverse(ExecState* exec, JSObject* thisObj, const List&) | |
281 | { | |
282 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
283 | unsigned middle = length / 2; | |
284 | ||
285 | for (unsigned k = 0; k < middle; k++) { | |
286 | unsigned lk1 = length - k - 1; | |
287 | JSValue* obj2 = getProperty(exec, thisObj, lk1); | |
288 | JSValue* obj = getProperty(exec, thisObj, k); | |
289 | ||
290 | if (obj2) | |
291 | thisObj->put(exec, k, obj2); | |
292 | else | |
293 | thisObj->deleteProperty(exec, k); | |
294 | ||
295 | if (obj) | |
296 | thisObj->put(exec, lk1, obj); | |
297 | else | |
298 | thisObj->deleteProperty(exec, lk1); | |
299 | } | |
300 | return thisObj; | |
301 | } | |
302 | ||
303 | JSValue* arrayProtoFuncShift(ExecState* exec, JSObject* thisObj, const List&) | |
304 | { | |
305 | JSValue* result = 0; | |
306 | ||
307 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
308 | if (length == 0) { | |
309 | thisObj->put(exec, exec->propertyNames().length, jsNumber(length)); | |
310 | result = jsUndefined(); | |
311 | } else { | |
312 | result = thisObj->get(exec, 0); | |
313 | for (unsigned k = 1; k < length; k++) { | |
314 | if (JSValue* obj = getProperty(exec, thisObj, k)) | |
315 | thisObj->put(exec, k - 1, obj); | |
316 | else | |
317 | thisObj->deleteProperty(exec, k - 1); | |
318 | } | |
319 | thisObj->deleteProperty(exec, length - 1); | |
320 | thisObj->put(exec, exec->propertyNames().length, jsNumber(length - 1)); | |
321 | } | |
322 | return result; | |
323 | } | |
324 | ||
325 | JSValue* arrayProtoFuncSlice(ExecState* exec, JSObject* thisObj, const List& args) | |
326 | { | |
327 | // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10 | |
328 | ||
329 | // We return a new array | |
330 | JSObject* resObj = static_cast<JSObject* >(exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, exec->emptyList())); | |
331 | JSValue* result = resObj; | |
332 | double begin = args[0]->toInteger(exec); | |
333 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
334 | if (begin >= 0) { | |
335 | if (begin > length) | |
336 | begin = length; | |
337 | } else { | |
338 | begin += length; | |
339 | if (begin < 0) | |
340 | begin = 0; | |
341 | } | |
342 | double end; | |
343 | if (args[1]->isUndefined()) | |
344 | end = length; | |
345 | else { | |
346 | end = args[1]->toInteger(exec); | |
347 | if (end < 0) { | |
348 | end += length; | |
349 | if (end < 0) | |
350 | end = 0; | |
351 | } else { | |
352 | if (end > length) | |
353 | end = length; | |
354 | } | |
355 | } | |
356 | ||
357 | int n = 0; | |
358 | int b = static_cast<int>(begin); | |
359 | int e = static_cast<int>(end); | |
360 | for (int k = b; k < e; k++, n++) { | |
361 | if (JSValue* v = getProperty(exec, thisObj, k)) | |
362 | resObj->put(exec, n, v); | |
363 | } | |
364 | resObj->put(exec, exec->propertyNames().length, jsNumber(n)); | |
365 | return result; | |
366 | } | |
367 | ||
368 | JSValue* arrayProtoFuncSort(ExecState* exec, JSObject* thisObj, const List& args) | |
369 | { | |
370 | JSObject* sortFunction = 0; | |
371 | if (!args[0]->isUndefined()) { | |
372 | sortFunction = args[0]->toObject(exec); | |
373 | if (!sortFunction->implementsCall()) | |
374 | sortFunction = 0; | |
375 | } | |
376 | ||
377 | if (thisObj->classInfo() == &ArrayInstance::info) { | |
378 | if (sortFunction) | |
379 | static_cast<ArrayInstance*>(thisObj)->sort(exec, sortFunction); | |
380 | else | |
381 | static_cast<ArrayInstance*>(thisObj)->sort(exec); | |
382 | return thisObj; | |
383 | } | |
384 | ||
385 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
386 | ||
387 | if (!length) | |
388 | return thisObj; | |
389 | ||
390 | // "Min" sort. Not the fastest, but definitely less code than heapsort | |
391 | // or quicksort, and much less swapping than bubblesort/insertionsort. | |
392 | for (unsigned i = 0; i < length - 1; ++i) { | |
393 | JSValue* iObj = thisObj->get(exec, i); | |
394 | unsigned themin = i; | |
395 | JSValue* minObj = iObj; | |
396 | for (unsigned j = i + 1; j < length; ++j) { | |
397 | JSValue* jObj = thisObj->get(exec, j); | |
398 | double compareResult; | |
399 | if (jObj->isUndefined()) | |
400 | compareResult = 1; // don't check minObj because there's no need to differentiate == (0) from > (1) | |
401 | else if (minObj->isUndefined()) | |
402 | compareResult = -1; | |
403 | else if (sortFunction) { | |
404 | List l; | |
405 | l.append(jObj); | |
406 | l.append(minObj); | |
407 | compareResult = sortFunction->call(exec, exec->dynamicGlobalObject(), l)->toNumber(exec); | |
408 | } else | |
409 | compareResult = (jObj->toString(exec) < minObj->toString(exec)) ? -1 : 1; | |
410 | ||
411 | if (compareResult < 0) { | |
412 | themin = j; | |
413 | minObj = jObj; | |
414 | } | |
415 | } | |
416 | // Swap themin and i | |
417 | if (themin > i) { | |
418 | thisObj->put(exec, i, minObj); | |
419 | thisObj->put(exec, themin, iObj); | |
420 | } | |
421 | } | |
422 | return thisObj; | |
423 | } | |
424 | ||
425 | JSValue* arrayProtoFuncSplice(ExecState* exec, JSObject* thisObj, const List& args) | |
426 | { | |
427 | // 15.4.4.12 | |
428 | JSObject* resObj = static_cast<JSObject* >(exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, exec->emptyList())); | |
429 | JSValue* result = resObj; | |
430 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
431 | if (!args.size()) | |
432 | return jsUndefined(); | |
433 | int begin = args[0]->toUInt32(exec); | |
434 | if (begin < 0) | |
435 | begin = std::max<int>(begin + length, 0); | |
436 | else | |
437 | begin = std::min<int>(begin, length); | |
438 | ||
439 | unsigned deleteCount; | |
440 | if (args.size() > 1) | |
441 | deleteCount = std::min<int>(std::max<int>(args[1]->toUInt32(exec), 0), length - begin); | |
442 | else | |
443 | deleteCount = length - begin; | |
444 | ||
445 | for (unsigned k = 0; k < deleteCount; k++) { | |
446 | if (JSValue* v = getProperty(exec, thisObj, k + begin)) | |
447 | resObj->put(exec, k, v); | |
448 | } | |
449 | resObj->put(exec, exec->propertyNames().length, jsNumber(deleteCount)); | |
450 | ||
451 | unsigned additionalArgs = std::max<int>(args.size() - 2, 0); | |
452 | if (additionalArgs != deleteCount) { | |
453 | if (additionalArgs < deleteCount) { | |
454 | for (unsigned k = begin; k < length - deleteCount; ++k) { | |
455 | if (JSValue* v = getProperty(exec, thisObj, k + deleteCount)) | |
456 | thisObj->put(exec, k + additionalArgs, v); | |
457 | else | |
458 | thisObj->deleteProperty(exec, k + additionalArgs); | |
459 | } | |
460 | for (unsigned k = length; k > length - deleteCount + additionalArgs; --k) | |
461 | thisObj->deleteProperty(exec, k - 1); | |
462 | } else { | |
463 | for (unsigned k = length - deleteCount; (int)k > begin; --k) { | |
464 | if (JSValue* obj = getProperty(exec, thisObj, k + deleteCount - 1)) | |
465 | thisObj->put(exec, k + additionalArgs - 1, obj); | |
466 | else | |
467 | thisObj->deleteProperty(exec, k + additionalArgs - 1); | |
468 | } | |
469 | } | |
470 | } | |
471 | for (unsigned k = 0; k < additionalArgs; ++k) | |
472 | thisObj->put(exec, k + begin, args[k + 2]); | |
473 | ||
474 | thisObj->put(exec, exec->propertyNames().length, jsNumber(length - deleteCount + additionalArgs)); | |
475 | return result; | |
476 | } | |
477 | ||
478 | JSValue* arrayProtoFuncUnShift(ExecState* exec, JSObject* thisObj, const List& args) | |
479 | { | |
480 | // 15.4.4.13 | |
481 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
482 | unsigned nrArgs = args.size(); | |
483 | if (nrArgs) { | |
484 | for (unsigned k = length; k > 0; --k) { | |
485 | if (JSValue* v = getProperty(exec, thisObj, k - 1)) | |
486 | thisObj->put(exec, k + nrArgs - 1, v); | |
487 | else | |
488 | thisObj->deleteProperty(exec, k + nrArgs - 1); | |
489 | } | |
490 | } | |
491 | for (unsigned k = 0; k < nrArgs; ++k) | |
492 | thisObj->put(exec, k, args[k]); | |
493 | JSValue* result = jsNumber(length + nrArgs); | |
494 | thisObj->put(exec, exec->propertyNames().length, result); | |
495 | return result; | |
496 | } | |
497 | ||
498 | JSValue* arrayProtoFuncFilter(ExecState* exec, JSObject* thisObj, const List& args) | |
499 | { | |
500 | JSObject* eachFunction = args[0]->toObject(exec); | |
501 | ||
502 | if (!eachFunction->implementsCall()) | |
503 | return throwError(exec, TypeError); | |
504 | ||
505 | JSObject* applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicGlobalObject() : args[1]->toObject(exec); | |
506 | JSObject* resultArray = static_cast<JSObject*>(exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, exec->emptyList())); | |
507 | ||
508 | unsigned filterIndex = 0; | |
509 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
510 | for (unsigned k = 0; k < length && !exec->hadException(); ++k) { | |
511 | PropertySlot slot; | |
512 | ||
513 | if (!thisObj->getPropertySlot(exec, k, slot)) | |
514 | continue; | |
515 | ||
516 | JSValue* v = slot.getValue(exec, thisObj, k); | |
517 | ||
518 | List eachArguments; | |
519 | ||
520 | eachArguments.append(v); | |
521 | eachArguments.append(jsNumber(k)); | |
522 | eachArguments.append(thisObj); | |
523 | ||
524 | JSValue* result = eachFunction->call(exec, applyThis, eachArguments); | |
525 | ||
526 | if (result->toBoolean(exec)) | |
527 | resultArray->put(exec, filterIndex++, v); | |
528 | } | |
529 | return resultArray; | |
530 | } | |
531 | ||
532 | JSValue* arrayProtoFuncMap(ExecState* exec, JSObject* thisObj, const List& args) | |
533 | { | |
534 | JSObject* eachFunction = args[0]->toObject(exec); | |
535 | if (!eachFunction->implementsCall()) | |
536 | return throwError(exec, TypeError); | |
537 | ||
538 | JSObject* applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicGlobalObject() : args[1]->toObject(exec); | |
539 | ||
540 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
541 | ||
542 | List mapArgs; | |
543 | mapArgs.append(jsNumber(length)); | |
544 | JSObject* resultArray = static_cast<JSObject*>(exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, mapArgs)); | |
545 | ||
546 | for (unsigned k = 0; k < length && !exec->hadException(); ++k) { | |
547 | PropertySlot slot; | |
548 | if (!thisObj->getPropertySlot(exec, k, slot)) | |
549 | continue; | |
550 | ||
551 | JSValue* v = slot.getValue(exec, thisObj, k); | |
552 | ||
553 | List eachArguments; | |
554 | ||
555 | eachArguments.append(v); | |
556 | eachArguments.append(jsNumber(k)); | |
557 | eachArguments.append(thisObj); | |
558 | ||
559 | JSValue* result = eachFunction->call(exec, applyThis, eachArguments); | |
560 | resultArray->put(exec, k, result); | |
561 | } | |
562 | ||
563 | return resultArray; | |
564 | } | |
565 | ||
566 | // Documentation for these three is available at: | |
567 | // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every | |
568 | // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach | |
569 | // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:some | |
570 | ||
571 | JSValue* arrayProtoFuncEvery(ExecState* exec, JSObject* thisObj, const List& args) | |
572 | { | |
573 | JSObject* eachFunction = args[0]->toObject(exec); | |
574 | ||
575 | if (!eachFunction->implementsCall()) | |
576 | return throwError(exec, TypeError); | |
577 | ||
578 | JSObject* applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicGlobalObject() : args[1]->toObject(exec); | |
579 | ||
580 | JSValue* result = jsBoolean(true); | |
581 | ||
582 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
583 | for (unsigned k = 0; k < length && !exec->hadException(); ++k) { | |
584 | PropertySlot slot; | |
585 | ||
586 | if (!thisObj->getPropertySlot(exec, k, slot)) | |
587 | continue; | |
588 | ||
589 | List eachArguments; | |
590 | ||
591 | eachArguments.append(slot.getValue(exec, thisObj, k)); | |
592 | eachArguments.append(jsNumber(k)); | |
593 | eachArguments.append(thisObj); | |
594 | ||
595 | bool predicateResult = eachFunction->call(exec, applyThis, eachArguments)->toBoolean(exec); | |
596 | ||
597 | if (!predicateResult) { | |
598 | result = jsBoolean(false); | |
599 | break; | |
600 | } | |
601 | } | |
602 | ||
603 | return result; | |
604 | } | |
605 | ||
606 | JSValue* arrayProtoFuncForEach(ExecState* exec, JSObject* thisObj, const List& args) | |
607 | { | |
608 | JSObject* eachFunction = args[0]->toObject(exec); | |
609 | ||
610 | if (!eachFunction->implementsCall()) | |
611 | return throwError(exec, TypeError); | |
612 | ||
613 | JSObject* applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicGlobalObject() : args[1]->toObject(exec); | |
614 | ||
615 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
616 | for (unsigned k = 0; k < length && !exec->hadException(); ++k) { | |
617 | PropertySlot slot; | |
618 | if (!thisObj->getPropertySlot(exec, k, slot)) | |
619 | continue; | |
620 | ||
621 | List eachArguments; | |
622 | eachArguments.append(slot.getValue(exec, thisObj, k)); | |
623 | eachArguments.append(jsNumber(k)); | |
624 | eachArguments.append(thisObj); | |
625 | ||
626 | eachFunction->call(exec, applyThis, eachArguments); | |
627 | } | |
628 | return jsUndefined(); | |
629 | } | |
630 | ||
631 | JSValue* arrayProtoFuncSome(ExecState* exec, JSObject* thisObj, const List& args) | |
632 | { | |
633 | JSObject* eachFunction = args[0]->toObject(exec); | |
634 | ||
635 | if (!eachFunction->implementsCall()) | |
636 | return throwError(exec, TypeError); | |
637 | ||
638 | JSObject* applyThis = args[1]->isUndefinedOrNull() ? exec->dynamicGlobalObject() : args[1]->toObject(exec); | |
639 | ||
640 | JSValue* result = jsBoolean(false); | |
641 | ||
642 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
643 | for (unsigned k = 0; k < length && !exec->hadException(); ++k) { | |
644 | PropertySlot slot; | |
645 | if (!thisObj->getPropertySlot(exec, k, slot)) | |
646 | continue; | |
647 | ||
648 | List eachArguments; | |
649 | eachArguments.append(slot.getValue(exec, thisObj, k)); | |
650 | eachArguments.append(jsNumber(k)); | |
651 | eachArguments.append(thisObj); | |
652 | ||
653 | bool predicateResult = eachFunction->call(exec, applyThis, eachArguments)->toBoolean(exec); | |
654 | ||
655 | if (predicateResult) { | |
656 | result = jsBoolean(true); | |
657 | break; | |
658 | } | |
659 | } | |
660 | return result; | |
661 | } | |
662 | ||
663 | JSValue* arrayProtoFuncIndexOf(ExecState* exec, JSObject* thisObj, const List& args) | |
664 | { | |
665 | // JavaScript 1.5 Extension by Mozilla | |
666 | // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf | |
667 | ||
668 | unsigned index = 0; | |
669 | double d = args[1]->toInteger(exec); | |
670 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
671 | if (d < 0) | |
672 | d += length; | |
673 | if (d > 0) { | |
674 | if (d > length) | |
675 | index = length; | |
676 | else | |
677 | index = static_cast<unsigned>(d); | |
678 | } | |
679 | ||
680 | JSValue* searchElement = args[0]; | |
681 | for (; index < length; ++index) { | |
682 | JSValue* e = getProperty(exec, thisObj, index); | |
683 | if (!e) | |
684 | continue; | |
685 | if (strictEqual(exec, searchElement, e)) | |
686 | return jsNumber(index); | |
687 | } | |
688 | ||
689 | return jsNumber(-1); | |
690 | } | |
691 | ||
692 | JSValue* arrayProtoFuncLastIndexOf(ExecState* exec, JSObject* thisObj, const List& args) | |
693 | { | |
694 | // JavaScript 1.6 Extension by Mozilla | |
695 | // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf | |
696 | ||
697 | unsigned length = thisObj->get(exec, exec->propertyNames().length)->toUInt32(exec); | |
698 | int index = length - 1; | |
699 | double d = args[1]->toIntegerPreserveNaN(exec); | |
700 | ||
701 | if (d < 0) { | |
702 | d += length; | |
703 | if (d < 0) | |
704 | return jsNumber(-1); | |
705 | } | |
706 | if (d < length) | |
707 | index = static_cast<int>(d); | |
708 | ||
709 | JSValue* searchElement = args[0]; | |
710 | for (; index >= 0; --index) { | |
711 | JSValue* e = getProperty(exec, thisObj, index); | |
712 | if (!e) | |
713 | continue; | |
714 | if (strictEqual(exec, searchElement, e)) | |
715 | return jsNumber(index); | |
716 | } | |
717 | ||
718 | return jsNumber(-1); | |
719 | } | |
720 | ||
721 | // ------------------------------ ArrayObjectImp ------------------------------- | |
722 | ||
723 | ArrayObjectImp::ArrayObjectImp(ExecState* exec, FunctionPrototype* funcProto, ArrayPrototype* arrayProto) | |
724 | : InternalFunctionImp(funcProto, arrayProto->classInfo()->className) | |
725 | { | |
726 | // ECMA 15.4.3.1 Array.prototype | |
727 | putDirect(exec->propertyNames().prototype, arrayProto, DontEnum|DontDelete|ReadOnly); | |
728 | ||
729 | // no. of arguments for constructor | |
730 | putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly|DontDelete|DontEnum); | |
731 | } | |
732 | ||
733 | bool ArrayObjectImp::implementsConstruct() const | |
734 | { | |
735 | return true; | |
736 | } | |
737 | ||
738 | // ECMA 15.4.2 | |
739 | JSObject* ArrayObjectImp::construct(ExecState* exec, const List& args) | |
740 | { | |
741 | // a single numeric argument denotes the array size (!) | |
742 | if (args.size() == 1 && args[0]->isNumber()) { | |
743 | uint32_t n = args[0]->toUInt32(exec); | |
744 | if (n != args[0]->toNumber(exec)) | |
745 | return throwError(exec, RangeError, "Array size is not a small enough positive integer."); | |
746 | return new ArrayInstance(exec->lexicalGlobalObject()->arrayPrototype(), n); | |
747 | } | |
748 | ||
749 | // otherwise the array is constructed with the arguments in it | |
750 | return new ArrayInstance(exec->lexicalGlobalObject()->arrayPrototype(), args); | |
751 | } | |
752 | ||
753 | // ECMA 15.6.1 | |
754 | JSValue* ArrayObjectImp::callAsFunction(ExecState* exec, JSObject*, const List& args) | |
755 | { | |
756 | // equivalent to 'new Array(....)' | |
757 | return construct(exec,args); | |
758 | } | |
759 | ||
760 | } |