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