41 |
/* |
/* |
42 |
* JS array class. |
* JS array class. |
43 |
* |
* |
44 |
* Array objects begin as "dense" arrays, optimized for numeric-only property |
* Array objects begin as "dense" arrays, optimized for index-only property |
45 |
* access over a vector of slots (obj->dslots) with high load factor. Array |
* access over a vector of slots (obj->dslots) with high load factor. Array |
46 |
* methods optimize for denseness by testing that the object's class is |
* methods optimize for denseness by testing that the object's class is |
47 |
* &js_ArrayClass, and can then directly manipulate the slots for efficiency. |
* &js_ArrayClass, and can then directly manipulate the slots for efficiency. |
49 |
* We track these pieces of metadata for arrays in dense mode: |
* We track these pieces of metadata for arrays in dense mode: |
50 |
* - the array's length property as a uint32, in JSSLOT_ARRAY_LENGTH, |
* - the array's length property as a uint32, in JSSLOT_ARRAY_LENGTH, |
51 |
* - the number of indices that are filled (non-holes), in JSSLOT_ARRAY_COUNT, |
* - the number of indices that are filled (non-holes), in JSSLOT_ARRAY_COUNT, |
52 |
* - the net number of slots starting at dslots (DENSELEN), in dslots[-1] if |
* - the net number of slots starting at dslots (capacity), in dslots[-1] if |
53 |
* dslots is non-NULL. |
* dslots is non-NULL. |
54 |
* |
* |
55 |
* In dense mode, holes in the array are represented by JSVAL_HOLE. The final |
* In dense mode, holes in the array are represented by JSVAL_HOLE. The final |
56 |
* slot in fslots (JSSLOT_ARRAY_LOOKUP_HOLDER) is used to store the single jsid |
* slot in fslots is unused. |
57 |
* "in use" by a lookupProperty caller. |
* |
58 |
|
* NB: the capacity and length of a dense array are entirely unrelated! The |
59 |
|
* length may be greater than, less than, or equal to the capacity. See |
60 |
|
* array_length_setter for an explanation of how the first, most surprising |
61 |
|
* case may occur. |
62 |
* |
* |
63 |
* Arrays are converted to use js_SlowArrayClass when any of these conditions |
* Arrays are converted to use js_SlowArrayClass when any of these conditions |
64 |
* are met: |
* are met: |
65 |
* - the load factor (COUNT / DENSELEN) is less than 0.25, and there are |
* - the load factor (COUNT / capacity) is less than 0.25, and there are |
66 |
* more than MIN_SPARSE_INDEX slots total |
* more than MIN_SPARSE_INDEX slots total |
67 |
* - a property is set that is non-numeric (and not "length"); or |
* - a property is set that is not indexed (and not "length"); or |
68 |
* - a hole is filled below DENSELEN (possibly implicitly through methods like |
* - a property is defined that has non-default property attributes. |
|
* |reverse| or |splice|). |
|
|
* |
|
|
* In the latter two cases, property creation order is no longer index order, |
|
|
* which necessitates use of a structure that keeps track of property creation |
|
|
* order. (ES4, due to expectations baked into web script, requires that |
|
|
* enumeration order be the order in which properties were created.) |
|
69 |
* |
* |
70 |
* An alternative in the latter case (out-of-order index set) would be to |
* Dense arrays do not track property creation order, so unlike other native |
71 |
* maintain the scope to track property enumeration order, but still use |
* objects and slow arrays, enumerating an array does not necessarily visit the |
72 |
* the fast slot access. That would have the same memory cost as just using |
* properties in the order they were created. We could instead maintain the |
73 |
* a js_SlowArrayClass, but have the same performance characteristics as |
* scope to track property enumeration order, but still use the fast slot |
74 |
* a dense array for slot accesses, at some cost in code complexity. |
* access. That would have the same memory cost as just using a |
75 |
|
* js_SlowArrayClass, but have the same performance characteristics as a dense |
76 |
|
* array for slot accesses, at some cost in code complexity. |
77 |
*/ |
*/ |
78 |
#include "jsstddef.h" |
#include "jsstddef.h" |
79 |
#include <stdlib.h> |
#include <stdlib.h> |
105 |
#define MAXSTR "4294967295" |
#define MAXSTR "4294967295" |
106 |
|
|
107 |
/* Small arrays are dense, no matter what. */ |
/* Small arrays are dense, no matter what. */ |
108 |
#define MIN_SPARSE_INDEX 32 |
#define MIN_SPARSE_INDEX 256 |
109 |
|
|
110 |
|
static inline bool |
111 |
|
INDEX_TOO_BIG(jsuint index) |
112 |
|
{ |
113 |
|
return index > JS_BIT(29) - 1; |
114 |
|
} |
115 |
|
|
|
#define INDEX_TOO_BIG(index) ((index) > JS_BIT(29) - 1) |
|
116 |
#define INDEX_TOO_SPARSE(array, index) \ |
#define INDEX_TOO_SPARSE(array, index) \ |
117 |
(INDEX_TOO_BIG(index) || \ |
(INDEX_TOO_BIG(index) || \ |
118 |
((index) > ARRAY_DENSE_LENGTH(array) && (index) >= MIN_SPARSE_INDEX && \ |
((index) > js_DenseArrayCapacity(array) && (index) >= MIN_SPARSE_INDEX && \ |
119 |
(index) > (uint32)((array)->fslots[JSSLOT_ARRAY_COUNT] + 1) * 4)) |
(index) > (uint32)((array)->fslots[JSSLOT_ARRAY_COUNT] + 1) * 4)) |
120 |
|
|
121 |
JS_STATIC_ASSERT(sizeof(JSScopeProperty) > 4 * sizeof(jsval)); |
JS_STATIC_ASSERT(sizeof(JSScopeProperty) > 4 * sizeof(jsval)); |
247 |
} |
} |
248 |
|
|
249 |
static JSBool |
static JSBool |
250 |
IndexToValue(JSContext *cx, jsuint index, jsval *vp) |
IndexToValue(JSContext *cx, jsdouble index, jsval *vp) |
251 |
{ |
{ |
252 |
if (index <= JSVAL_INT_MAX) { |
return js_NewWeaklyRootedNumber(cx, index, vp); |
|
*vp = INT_TO_JSVAL(index); |
|
|
return JS_TRUE; |
|
|
} |
|
|
return JS_NewDoubleValue(cx, (jsdouble)index, vp); |
|
253 |
} |
} |
254 |
|
|
255 |
JSBool JS_FASTCALL |
JSBool JS_FASTCALL |
313 |
} |
} |
314 |
|
|
315 |
static JSBool |
static JSBool |
316 |
ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldlen, uint32 len) |
ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldsize, uint32 size) |
317 |
{ |
{ |
318 |
jsval *slots, *newslots; |
jsval *slots, *newslots; |
319 |
|
|
320 |
if (len == 0) { |
if (size == 0) { |
321 |
if (obj->dslots) { |
if (obj->dslots) { |
322 |
JS_free(cx, obj->dslots - 1); |
JS_free(cx, obj->dslots - 1); |
323 |
obj->dslots = NULL; |
obj->dslots = NULL; |
325 |
return JS_TRUE; |
return JS_TRUE; |
326 |
} |
} |
327 |
|
|
328 |
if (len > ~(uint32)0 / sizeof(jsval)) { |
/* |
329 |
|
* MAX_DSLOTS_LENGTH is the maximum net capacity supported. Since we allocate |
330 |
|
* one additional slot to hold the array length, we have to use >= here. |
331 |
|
*/ |
332 |
|
if (size >= MAX_DSLOTS_LENGTH) { |
333 |
js_ReportAllocationOverflow(cx); |
js_ReportAllocationOverflow(cx); |
334 |
return JS_FALSE; |
return JS_FALSE; |
335 |
} |
} |
336 |
|
|
337 |
slots = obj->dslots ? obj->dslots - 1 : NULL; |
slots = obj->dslots ? obj->dslots - 1 : NULL; |
338 |
newslots = (jsval *) JS_realloc(cx, slots, sizeof (jsval) * (len + 1)); |
newslots = (jsval *) JS_realloc(cx, slots, (size + 1) * sizeof(jsval)); |
339 |
if (!newslots) |
if (!newslots) |
340 |
return JS_FALSE; |
return JS_FALSE; |
341 |
|
|
342 |
obj->dslots = newslots + 1; |
obj->dslots = newslots + 1; |
343 |
ARRAY_SET_DENSE_LENGTH(obj, len); |
js_SetDenseArrayCapacity(obj, size); |
344 |
|
|
345 |
for (slots = obj->dslots + oldlen; slots < obj->dslots + len; slots++) |
for (slots = obj->dslots + oldsize; slots < obj->dslots + size; slots++) |
346 |
*slots = JSVAL_HOLE; |
*slots = JSVAL_HOLE; |
347 |
|
|
348 |
return JS_TRUE; |
return JS_TRUE; |
349 |
} |
} |
350 |
|
|
351 |
|
/* |
352 |
|
* When a dense array with CAPACITY_DOUBLING_MAX or fewer slots needs to grow, |
353 |
|
* double its capacity, to push() N elements in amortized O(N) time. |
354 |
|
* |
355 |
|
* Above this limit, grow by 12.5% each time. Speed is still amortized O(N), |
356 |
|
* with a higher constant factor, and we waste less space. |
357 |
|
*/ |
358 |
|
#define CAPACITY_DOUBLING_MAX (1024 * 1024) |
359 |
|
|
360 |
|
/* |
361 |
|
* Round up all large allocations to a multiple of this (1MB), so as not to |
362 |
|
* waste space if malloc gives us 1MB-sized chunks (as jemalloc does). |
363 |
|
*/ |
364 |
|
#define CAPACITY_CHUNK (1024 * 1024 / sizeof(jsval)) |
365 |
|
|
366 |
static JSBool |
static JSBool |
367 |
EnsureLength(JSContext *cx, JSObject *obj, uint32 len) |
EnsureCapacity(JSContext *cx, JSObject *obj, uint32 capacity) |
368 |
{ |
{ |
369 |
uint32 oldlen = ARRAY_DENSE_LENGTH(obj); |
uint32 oldsize = js_DenseArrayCapacity(obj); |
370 |
|
|
371 |
if (len > oldlen) { |
if (capacity > oldsize) { |
372 |
return ResizeSlots(cx, obj, oldlen, |
/* |
373 |
len + ARRAY_GROWBY - (len % ARRAY_GROWBY)); |
* If this overflows uint32, capacity is very large. nextsize will end |
374 |
|
* up being less than capacity, the code below will thus disregard it, |
375 |
|
* and ResizeSlots will fail. |
376 |
|
* |
377 |
|
* The way we use dslots[-1] forces a few +1s and -1s here. For |
378 |
|
* example, (oldsize * 2 + 1) produces the sequence 7, 15, 31, 63, ... |
379 |
|
* which makes the total allocation size (with dslots[-1]) a power |
380 |
|
* of two. |
381 |
|
*/ |
382 |
|
uint32 nextsize = (oldsize <= CAPACITY_DOUBLING_MAX) |
383 |
|
? oldsize * 2 + 1 |
384 |
|
: oldsize + (oldsize >> 3); |
385 |
|
|
386 |
|
capacity = JS_MAX(capacity, nextsize); |
387 |
|
if (capacity >= CAPACITY_CHUNK) |
388 |
|
capacity = JS_ROUNDUP(capacity + 1, CAPACITY_CHUNK) - 1; /* -1 for dslots[-1] */ |
389 |
|
else if (capacity < ARRAY_CAPACITY_MIN) |
390 |
|
capacity = ARRAY_CAPACITY_MIN; |
391 |
|
return ResizeSlots(cx, obj, oldsize, capacity); |
392 |
} |
} |
393 |
return JS_TRUE; |
return JS_TRUE; |
394 |
} |
} |
395 |
|
|
396 |
|
static bool |
397 |
|
ReallyBigIndexToId(JSContext* cx, jsdouble index, jsid* idp) |
398 |
|
{ |
399 |
|
JSAutoTempValueRooter dval(cx); |
400 |
|
if (!js_NewDoubleInRootedValue(cx, index, dval.addr()) || |
401 |
|
!js_ValueToStringId(cx, dval.value(), idp)) { |
402 |
|
return JS_FALSE; |
403 |
|
} |
404 |
|
return JS_TRUE; |
405 |
|
} |
406 |
|
|
407 |
|
static bool |
408 |
|
IndexToId(JSContext* cx, JSObject* obj, jsdouble index, JSBool* hole, jsid* idp, |
409 |
|
JSBool createAtom = JS_FALSE) |
410 |
|
{ |
411 |
|
if (index <= JSVAL_INT_MAX) { |
412 |
|
*idp = INT_TO_JSID(index); |
413 |
|
return JS_TRUE; |
414 |
|
} |
415 |
|
|
416 |
|
if (index <= jsuint(-1)) { |
417 |
|
if (!BigIndexToId(cx, obj, jsuint(index), createAtom, idp)) |
418 |
|
return JS_FALSE; |
419 |
|
if (hole && JSVAL_IS_VOID(*idp)) |
420 |
|
*hole = JS_TRUE; |
421 |
|
return JS_TRUE; |
422 |
|
} |
423 |
|
|
424 |
|
return ReallyBigIndexToId(cx, index, idp); |
425 |
|
} |
426 |
|
|
427 |
/* |
/* |
428 |
* If the property at the given index exists, get its value into location |
* If the property at the given index exists, get its value into location |
429 |
* pointed by vp and set *hole to false. Otherwise set *hole to true and *vp |
* pointed by vp and set *hole to false. Otherwise set *hole to true and *vp |
431 |
* properly rooted and can be used as GC-protected storage for temporaries. |
* properly rooted and can be used as GC-protected storage for temporaries. |
432 |
*/ |
*/ |
433 |
static JSBool |
static JSBool |
434 |
GetArrayElement(JSContext *cx, JSObject *obj, jsuint index, JSBool *hole, |
GetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole, |
435 |
jsval *vp) |
jsval *vp) |
436 |
{ |
{ |
437 |
jsid id; |
JS_ASSERT(index >= 0); |
438 |
JSObject *obj2; |
if (OBJ_IS_DENSE_ARRAY(cx, obj) && index < js_DenseArrayCapacity(obj) && |
439 |
JSProperty *prop; |
(*vp = obj->dslots[jsuint(index)]) != JSVAL_HOLE) { |
|
|
|
|
if (OBJ_IS_DENSE_ARRAY(cx, obj) && index < ARRAY_DENSE_LENGTH(obj) && |
|
|
(*vp = obj->dslots[index]) != JSVAL_HOLE) { |
|
440 |
*hole = JS_FALSE; |
*hole = JS_FALSE; |
441 |
return JS_TRUE; |
return JS_TRUE; |
442 |
} |
} |
443 |
|
|
444 |
if (index <= JSVAL_INT_MAX) { |
JSAutoTempIdRooter idr(cx); |
445 |
id = INT_TO_JSID(index); |
|
446 |
} else { |
*hole = JS_FALSE; |
447 |
if (!BigIndexToId(cx, obj, index, JS_FALSE, &id)) |
if (!IndexToId(cx, obj, index, hole, idr.addr())) |
448 |
return JS_FALSE; |
return JS_FALSE; |
449 |
if (JSVAL_IS_VOID(id)) { |
if (*hole) { |
450 |
*hole = JS_TRUE; |
*vp = JSVAL_VOID; |
451 |
*vp = JSVAL_VOID; |
return JS_TRUE; |
|
return JS_TRUE; |
|
|
} |
|
452 |
} |
} |
453 |
|
|
454 |
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) |
JSObject *obj2; |
455 |
|
JSProperty *prop; |
456 |
|
if (!OBJ_LOOKUP_PROPERTY(cx, obj, idr.id(), &obj2, &prop)) |
457 |
return JS_FALSE; |
return JS_FALSE; |
458 |
if (!prop) { |
if (!prop) { |
459 |
*hole = JS_TRUE; |
*hole = JS_TRUE; |
460 |
*vp = JSVAL_VOID; |
*vp = JSVAL_VOID; |
461 |
} else { |
} else { |
462 |
OBJ_DROP_PROPERTY(cx, obj2, prop); |
OBJ_DROP_PROPERTY(cx, obj2, prop); |
463 |
if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) |
if (!OBJ_GET_PROPERTY(cx, obj, idr.id(), vp)) |
464 |
return JS_FALSE; |
return JS_FALSE; |
465 |
*hole = JS_FALSE; |
*hole = JS_FALSE; |
466 |
} |
} |
471 |
* Set the value of the property at the given index to v assuming v is rooted. |
* Set the value of the property at the given index to v assuming v is rooted. |
472 |
*/ |
*/ |
473 |
static JSBool |
static JSBool |
474 |
SetArrayElement(JSContext *cx, JSObject *obj, jsuint index, jsval v) |
SetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, jsval v) |
475 |
{ |
{ |
476 |
jsid id; |
JS_ASSERT(index >= 0); |
477 |
|
|
478 |
if (OBJ_IS_DENSE_ARRAY(cx, obj)) { |
if (OBJ_IS_DENSE_ARRAY(cx, obj)) { |
479 |
/* Predicted/prefeched code should favor the remains-dense case. */ |
/* Predicted/prefetched code should favor the remains-dense case. */ |
480 |
if (!INDEX_TOO_SPARSE(obj, index)) { |
if (index <= jsuint(-1)) { |
481 |
if (!EnsureLength(cx, obj, index + 1)) |
jsuint idx = jsuint(index); |
482 |
return JS_FALSE; |
if (!INDEX_TOO_SPARSE(obj, idx)) { |
483 |
if (index >= (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH]) |
JS_ASSERT(idx + 1 > idx); |
484 |
obj->fslots[JSSLOT_ARRAY_LENGTH] = index + 1; |
if (!EnsureCapacity(cx, obj, idx + 1)) |
485 |
if (obj->dslots[index] == JSVAL_HOLE) |
return JS_FALSE; |
486 |
obj->fslots[JSSLOT_ARRAY_COUNT]++; |
if (idx >= uint32(obj->fslots[JSSLOT_ARRAY_LENGTH])) |
487 |
obj->dslots[index] = v; |
obj->fslots[JSSLOT_ARRAY_LENGTH] = idx + 1; |
488 |
return JS_TRUE; |
if (obj->dslots[idx] == JSVAL_HOLE) |
489 |
|
obj->fslots[JSSLOT_ARRAY_COUNT]++; |
490 |
|
obj->dslots[idx] = v; |
491 |
|
return JS_TRUE; |
492 |
|
} |
493 |
} |
} |
494 |
|
|
495 |
if (!js_MakeArraySlow(cx, obj)) |
if (!js_MakeArraySlow(cx, obj)) |
496 |
return JS_FALSE; |
return JS_FALSE; |
497 |
} |
} |
498 |
|
|
499 |
if (index <= JSVAL_INT_MAX) { |
JSAutoTempIdRooter idr(cx); |
500 |
id = INT_TO_JSID(index); |
|
501 |
} else { |
if (!IndexToId(cx, obj, index, NULL, idr.addr(), JS_TRUE)) |
502 |
if (!BigIndexToId(cx, obj, index, JS_TRUE, &id)) |
return JS_FALSE; |
503 |
return JS_FALSE; |
JS_ASSERT(!JSVAL_IS_VOID(idr.id())); |
504 |
JS_ASSERT(!JSVAL_IS_VOID(id)); |
|
505 |
} |
return OBJ_SET_PROPERTY(cx, obj, idr.id(), &v); |
|
return OBJ_SET_PROPERTY(cx, obj, id, &v); |
|
506 |
} |
} |
507 |
|
|
508 |
static JSBool |
static JSBool |
509 |
DeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index) |
DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index) |
510 |
{ |
{ |
511 |
jsid id; |
JS_ASSERT(index >= 0); |
|
jsval junk; |
|
|
|
|
512 |
if (OBJ_IS_DENSE_ARRAY(cx, obj)) { |
if (OBJ_IS_DENSE_ARRAY(cx, obj)) { |
513 |
if (index < ARRAY_DENSE_LENGTH(obj)) { |
if (index <= jsuint(-1)) { |
514 |
if (obj->dslots[index] != JSVAL_HOLE) |
jsuint idx = jsuint(index); |
515 |
obj->fslots[JSSLOT_ARRAY_COUNT]--; |
if (!INDEX_TOO_SPARSE(obj, idx) && idx < js_DenseArrayCapacity(obj)) { |
516 |
obj->dslots[index] = JSVAL_HOLE; |
if (obj->dslots[idx] != JSVAL_HOLE) |
517 |
|
obj->fslots[JSSLOT_ARRAY_COUNT]--; |
518 |
|
obj->dslots[idx] = JSVAL_HOLE; |
519 |
|
return JS_TRUE; |
520 |
|
} |
521 |
} |
} |
522 |
return JS_TRUE; |
return JS_TRUE; |
523 |
} |
} |
524 |
|
|
525 |
if (index <= JSVAL_INT_MAX) { |
JSAutoTempIdRooter idr(cx); |
526 |
id = INT_TO_JSID(index); |
|
527 |
} else { |
if (!IndexToId(cx, obj, index, NULL, idr.addr())) |
528 |
if (!BigIndexToId(cx, obj, index, JS_FALSE, &id)) |
return JS_FALSE; |
529 |
return JS_FALSE; |
if (JSVAL_IS_VOID(idr.id())) |
530 |
if (JSVAL_IS_VOID(id)) |
return JS_TRUE; |
531 |
return JS_TRUE; |
|
532 |
} |
jsval junk; |
533 |
return OBJ_DELETE_PROPERTY(cx, obj, id, &junk); |
return OBJ_DELETE_PROPERTY(cx, obj, idr.id(), &junk); |
534 |
} |
} |
535 |
|
|
536 |
/* |
/* |
538 |
* its value to v assuming v is rooted. |
* its value to v assuming v is rooted. |
539 |
*/ |
*/ |
540 |
static JSBool |
static JSBool |
541 |
SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index, |
SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index, |
542 |
JSBool hole, jsval v) |
JSBool hole, jsval v) |
543 |
{ |
{ |
544 |
if (hole) { |
if (hole) { |
549 |
} |
} |
550 |
|
|
551 |
JSBool |
JSBool |
552 |
js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length) |
js_SetLengthProperty(JSContext *cx, JSObject *obj, jsdouble length) |
553 |
{ |
{ |
554 |
jsval v; |
jsval v; |
555 |
jsid id; |
jsid id; |
651 |
} |
} |
652 |
|
|
653 |
if (OBJ_IS_DENSE_ARRAY(cx, obj)) { |
if (OBJ_IS_DENSE_ARRAY(cx, obj)) { |
654 |
if (ARRAY_DENSE_LENGTH(obj) && !ResizeSlots(cx, obj, oldlen, newlen)) |
/* Don't reallocate if we're not actually shrinking our slots. */ |
655 |
|
jsuint oldsize = js_DenseArrayCapacity(obj); |
656 |
|
if (oldsize >= newlen && !ResizeSlots(cx, obj, oldsize, newlen)) |
657 |
return JS_FALSE; |
return JS_FALSE; |
658 |
} else if (oldlen - newlen < (1 << 24)) { |
} else if (oldlen - newlen < (1 << 24)) { |
659 |
do { |
do { |
660 |
--oldlen; |
--oldlen; |
661 |
if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) || |
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
662 |
!DeleteArrayElement(cx, obj, oldlen)) { |
!DeleteArrayElement(cx, obj, oldlen)) { |
663 |
return JS_FALSE; |
return JS_FALSE; |
664 |
} |
} |
679 |
JS_PUSH_TEMP_ROOT_OBJECT(cx, iter, &tvr); |
JS_PUSH_TEMP_ROOT_OBJECT(cx, iter, &tvr); |
680 |
gap = oldlen - newlen; |
gap = oldlen - newlen; |
681 |
for (;;) { |
for (;;) { |
682 |
ok = (JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) && |
ok = (JS_CHECK_OPERATION_LIMIT(cx) && |
683 |
JS_NextProperty(cx, iter, &id)); |
JS_NextProperty(cx, iter, &id)); |
684 |
if (!ok) |
if (!ok) |
685 |
break; |
break; |
700 |
return JS_TRUE; |
return JS_TRUE; |
701 |
} |
} |
702 |
|
|
703 |
|
/* |
704 |
|
* We have only indexed properties up to capacity (excepting holes), plus the |
705 |
|
* length property. For all else, we delegate to the prototype. |
706 |
|
*/ |
707 |
|
static inline bool |
708 |
|
IsDenseArrayId(JSContext *cx, JSObject *obj, jsid id) |
709 |
|
{ |
710 |
|
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); |
711 |
|
|
712 |
|
uint32 i; |
713 |
|
return id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) || |
714 |
|
(js_IdIsIndex(id, &i) && |
715 |
|
obj->fslots[JSSLOT_ARRAY_LENGTH] != 0 && |
716 |
|
i < js_DenseArrayCapacity(obj) && |
717 |
|
obj->dslots[i] != JSVAL_HOLE); |
718 |
|
} |
719 |
|
|
720 |
static JSBool |
static JSBool |
721 |
array_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, |
array_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, |
722 |
JSProperty **propp) |
JSProperty **propp) |
723 |
{ |
{ |
|
uint32 i; |
|
|
union { JSProperty *p; jsval *v; } u; |
|
|
|
|
724 |
if (!OBJ_IS_DENSE_ARRAY(cx, obj)) |
if (!OBJ_IS_DENSE_ARRAY(cx, obj)) |
725 |
return js_LookupProperty(cx, obj, id, objp, propp); |
return js_LookupProperty(cx, obj, id, objp, propp); |
726 |
|
|
727 |
/* |
if (IsDenseArrayId(cx, obj, id)) { |
728 |
* We have only indexed properties up to DENSELEN (excepting holes), plus |
*propp = (JSProperty *) id; |
729 |
* the length property. For all else, we delegate to the prototype. |
*objp = obj; |
730 |
*/ |
return JS_TRUE; |
|
if (id != ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) && |
|
|
(!js_IdIsIndex(id, &i) || |
|
|
obj->fslots[JSSLOT_ARRAY_LENGTH] == 0 || |
|
|
i >= ARRAY_DENSE_LENGTH(obj) || |
|
|
obj->dslots[i] == JSVAL_HOLE)) |
|
|
{ |
|
|
JSObject *proto = STOBJ_GET_PROTO(obj); |
|
|
|
|
|
if (!proto) { |
|
|
*objp = NULL; |
|
|
*propp = NULL; |
|
|
return JS_TRUE; |
|
|
} |
|
|
|
|
|
return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); |
|
731 |
} |
} |
732 |
|
|
733 |
/* FIXME 417501: threadsafety: could race with a lookup on another thread. |
JSObject *proto = STOBJ_GET_PROTO(obj); |
734 |
* If we can only have a single lookup active per context, we could |
if (!proto) { |
735 |
* pigeonhole this on the context instead. */ |
*objp = NULL; |
736 |
JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER])); |
*propp = NULL; |
737 |
obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER] = (jsval) id; |
return JS_TRUE; |
738 |
u.v = &(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]); |
} |
739 |
*propp = u.p; |
return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); |
|
*objp = obj; |
|
|
return JS_TRUE; |
|
740 |
} |
} |
741 |
|
|
742 |
static void |
static void |
743 |
array_dropProperty(JSContext *cx, JSObject *obj, JSProperty *prop) |
array_dropProperty(JSContext *cx, JSObject *obj, JSProperty *prop) |
744 |
{ |
{ |
745 |
JS_ASSERT_IF(OBJ_IS_DENSE_ARRAY(cx, obj), |
JS_ASSERT(IsDenseArrayId(cx, obj, (jsid) prop)); |
746 |
!JSVAL_IS_VOID(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER])); |
} |
747 |
#ifdef DEBUG |
|
748 |
obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER] = JSVAL_VOID; |
JSBool |
749 |
#endif |
js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, JSProperty *prop, |
750 |
|
jsval *vp) |
751 |
|
{ |
752 |
|
jsid id = (jsid) prop; |
753 |
|
JS_ASSERT(IsDenseArrayId(cx, obj, id)); |
754 |
|
|
755 |
|
uint32 i; |
756 |
|
if (!js_IdIsIndex(id, &i)) { |
757 |
|
JS_ASSERT(id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)); |
758 |
|
return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp); |
759 |
|
} |
760 |
|
*vp = obj->dslots[i]; |
761 |
|
return JS_TRUE; |
762 |
} |
} |
763 |
|
|
764 |
static JSBool |
static JSBool |
777 |
if (!OBJ_IS_DENSE_ARRAY(cx, obj)) |
if (!OBJ_IS_DENSE_ARRAY(cx, obj)) |
778 |
return js_GetProperty(cx, obj, id, vp); |
return js_GetProperty(cx, obj, id, vp); |
779 |
|
|
780 |
if (!js_IdIsIndex(ID_TO_VALUE(id), &i) || i >= ARRAY_DENSE_LENGTH(obj) || |
if (!js_IdIsIndex(ID_TO_VALUE(id), &i) || i >= js_DenseArrayCapacity(obj) || |
781 |
obj->dslots[i] == JSVAL_HOLE) { |
obj->dslots[i] == JSVAL_HOLE) { |
782 |
JSObject *obj2; |
JSObject *obj2; |
783 |
JSProperty *prop; |
JSProperty *prop; |
863 |
return js_SetProperty(cx, obj, id, vp); |
return js_SetProperty(cx, obj, id, vp); |
864 |
} |
} |
865 |
|
|
866 |
if (!EnsureLength(cx, obj, i + 1)) |
if (!EnsureCapacity(cx, obj, i + 1)) |
867 |
return JS_FALSE; |
return JS_FALSE; |
868 |
|
|
869 |
if (i >= (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH]) |
if (i >= (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH]) |
874 |
return JS_TRUE; |
return JS_TRUE; |
875 |
} |
} |
876 |
|
|
877 |
|
JSBool |
878 |
|
js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj) |
879 |
|
{ |
880 |
|
/* |
881 |
|
* Walk up the prototype chain and see if this indexed element already |
882 |
|
* exists. If we hit the end of the prototype chain, it's safe to set the |
883 |
|
* element on the original object. |
884 |
|
*/ |
885 |
|
while ((obj = JSVAL_TO_OBJECT(obj->fslots[JSSLOT_PROTO])) != NULL) { |
886 |
|
/* |
887 |
|
* If the prototype is a non-native object (possibly a dense array), or |
888 |
|
* a native object (possibly a slow array) that has indexed properties, |
889 |
|
* return true. |
890 |
|
*/ |
891 |
|
if (!OBJ_IS_NATIVE(obj)) |
892 |
|
return JS_TRUE; |
893 |
|
if (SCOPE_HAS_INDEXED_PROPERTIES(OBJ_SCOPE(obj))) |
894 |
|
return JS_TRUE; |
895 |
|
} |
896 |
|
return JS_FALSE; |
897 |
|
} |
898 |
|
|
899 |
#ifdef JS_TRACER |
#ifdef JS_TRACER |
900 |
JSBool FASTCALL |
JSBool FASTCALL |
901 |
js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, jsval v) |
js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, jsval v) |
902 |
{ |
{ |
903 |
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); |
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); |
904 |
|
|
905 |
do { |
/* |
906 |
jsuint length = ARRAY_DENSE_LENGTH(obj); |
* Let the interpreter worry about negative array indexes. |
907 |
if ((jsuint)i < length) { |
*/ |
908 |
if (obj->dslots[i] == JSVAL_HOLE) { |
JS_ASSERT((MAX_DSLOTS_LENGTH > JSVAL_INT_MAX) == (sizeof(jsval) != sizeof(uint32))); |
909 |
if (cx->runtime->anyArrayProtoHasElement) |
if (MAX_DSLOTS_LENGTH > JSVAL_INT_MAX) { |
910 |
break; |
/* |
911 |
if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH]) |
* Have to check for negative values bleeding through on 64-bit machines only, |
912 |
obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1; |
* since we can't allocate large enough arrays for this on 32-bit machines. |
913 |
obj->fslots[JSSLOT_ARRAY_COUNT]++; |
*/ |
914 |
} |
if (i < 0) |
915 |
obj->dslots[i] = v; |
return JS_FALSE; |
916 |
return JS_TRUE; |
} |
917 |
} |
|
918 |
} while (0); |
/* |
919 |
return OBJ_SET_PROPERTY(cx, obj, INT_TO_JSID(i), &v); |
* If needed, grow the array as long it remains dense, otherwise fall off trace. |
920 |
|
*/ |
921 |
|
jsuint u = jsuint(i); |
922 |
|
jsuint capacity = js_DenseArrayCapacity(obj); |
923 |
|
if ((u >= capacity) && (INDEX_TOO_SPARSE(obj, u) || !EnsureCapacity(cx, obj, u + 1))) |
924 |
|
return JS_FALSE; |
925 |
|
|
926 |
|
if (obj->dslots[u] == JSVAL_HOLE) { |
927 |
|
if (js_PrototypeHasIndexedProperties(cx, obj)) |
928 |
|
return JS_FALSE; |
929 |
|
|
930 |
|
if (u >= jsuint(obj->fslots[JSSLOT_ARRAY_LENGTH])) |
931 |
|
obj->fslots[JSSLOT_ARRAY_LENGTH] = u + 1; |
932 |
|
++obj->fslots[JSSLOT_ARRAY_COUNT]; |
933 |
|
} |
934 |
|
|
935 |
|
obj->dslots[u] = v; |
936 |
|
return JS_TRUE; |
937 |
} |
} |
938 |
#endif |
#endif |
939 |
|
|
952 |
if (!isIndex || attrs != JSPROP_ENUMERATE) { |
if (!isIndex || attrs != JSPROP_ENUMERATE) { |
953 |
if (!ENSURE_SLOW_ARRAY(cx, obj)) |
if (!ENSURE_SLOW_ARRAY(cx, obj)) |
954 |
return JS_FALSE; |
return JS_FALSE; |
|
if (isIndex && STOBJ_IS_DELEGATE(obj)) |
|
|
cx->runtime->anyArrayProtoHasElement = JS_TRUE; |
|
955 |
return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, propp); |
return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, propp); |
956 |
} |
} |
957 |
|
|
989 |
return JS_TRUE; |
return JS_TRUE; |
990 |
} |
} |
991 |
|
|
992 |
if (js_IdIsIndex(id, &i) && i < ARRAY_DENSE_LENGTH(obj) && |
if (js_IdIsIndex(id, &i) && i < js_DenseArrayCapacity(obj) && |
993 |
obj->dslots[i] != JSVAL_HOLE) { |
obj->dslots[i] != JSVAL_HOLE) { |
994 |
obj->fslots[JSSLOT_ARRAY_COUNT]--; |
obj->fslots[JSSLOT_ARRAY_COUNT]--; |
995 |
obj->dslots[i] = JSVAL_HOLE; |
obj->dslots[i] = JSVAL_HOLE; |
1064 |
array_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, |
array_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, |
1065 |
jsval *statep, jsid *idp) |
jsval *statep, jsid *idp) |
1066 |
{ |
{ |
1067 |
uint32 length, i; |
uint32 capacity, i; |
1068 |
JSIndexIterState *ii; |
JSIndexIterState *ii; |
1069 |
|
|
1070 |
switch (enum_op) { |
switch (enum_op) { |
1071 |
case JSENUMERATE_INIT: |
case JSENUMERATE_INIT: |
1072 |
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); |
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); |
1073 |
length = ARRAY_DENSE_LENGTH(obj); |
capacity = js_DenseArrayCapacity(obj); |
1074 |
if (idp) |
if (idp) |
1075 |
*idp = INT_TO_JSVAL(obj->fslots[JSSLOT_ARRAY_COUNT]); |
*idp = INT_TO_JSVAL(obj->fslots[JSSLOT_ARRAY_COUNT]); |
1076 |
ii = NULL; |
ii = NULL; |
1077 |
for (i = 0; i != length; ++i) { |
for (i = 0; i != capacity; ++i) { |
1078 |
if (obj->dslots[i] == JSVAL_HOLE) { |
if (obj->dslots[i] == JSVAL_HOLE) { |
1079 |
if (!ii) { |
if (!ii) { |
1080 |
ii = (JSIndexIterState *) |
ii = (JSIndexIterState *) |
1081 |
JS_malloc(cx, offsetof(JSIndexIterState, holes) + |
JS_malloc(cx, offsetof(JSIndexIterState, holes) + |
1082 |
JS_BITMAP_SIZE(length)); |
JS_BITMAP_SIZE(capacity)); |
1083 |
if (!ii) |
if (!ii) |
1084 |
return JS_FALSE; |
return JS_FALSE; |
1085 |
ii->hasHoles = JS_TRUE; |
ii->hasHoles = JS_TRUE; |
1086 |
memset(ii->holes, 0, JS_BITMAP_SIZE(length)); |
memset(ii->holes, 0, JS_BITMAP_SIZE(capacity)); |
1087 |
} |
} |
1088 |
JS_SET_BIT(ii->holes, i); |
JS_SET_BIT(ii->holes, i); |
1089 |
} |
} |
1090 |
} |
} |
1091 |
if (!ii) { |
if (!ii) { |
1092 |
/* Array has no holes. */ |
/* Array has no holes. */ |
1093 |
if (length <= PACKED_UINT_PAIR_MASK) { |
if (capacity <= PACKED_UINT_PAIR_MASK) { |
1094 |
*statep = UINT_PAIR_TO_BOOLEAN_JSVAL(0, length); |
*statep = UINT_PAIR_TO_BOOLEAN_JSVAL(0, capacity); |
1095 |
break; |
break; |
1096 |
} |
} |
1097 |
ii = (JSIndexIterState *) |
ii = (JSIndexIterState *) |
1101 |
ii->hasHoles = JS_FALSE; |
ii->hasHoles = JS_FALSE; |
1102 |
} |
} |
1103 |
ii->index = 0; |
ii->index = 0; |
1104 |
ii->length = length; |
ii->length = capacity; |
1105 |
*statep = (jsval) ii | INDEX_ITER_TAG; |
*statep = (jsval) ii | INDEX_ITER_TAG; |
1106 |
JS_ASSERT(*statep & JSVAL_INT); |
JS_ASSERT(*statep & JSVAL_INT); |
1107 |
break; |
break; |
1108 |
|
|
1109 |
case JSENUMERATE_NEXT: |
case JSENUMERATE_NEXT: |
1110 |
if (JSVAL_TAG(*statep) == JSVAL_BOOLEAN) { |
if (JSVAL_TAG(*statep) == JSVAL_BOOLEAN) { |
1111 |
BOOLEAN_JSVAL_TO_UINT_PAIR(*statep, i, length); |
BOOLEAN_JSVAL_TO_UINT_PAIR(*statep, i, capacity); |
1112 |
if (i != length) { |
if (i != capacity) { |
1113 |
*idp = INT_TO_JSID(i); |
*idp = INT_TO_JSID(i); |
1114 |
*statep = UINT_PAIR_TO_BOOLEAN_JSVAL(i + 1, length); |
*statep = UINT_PAIR_TO_BOOLEAN_JSVAL(i + 1, capacity); |
1115 |
break; |
break; |
1116 |
} |
} |
1117 |
} else { |
} else { |
1174 |
static void |
static void |
1175 |
array_trace(JSTracer *trc, JSObject *obj) |
array_trace(JSTracer *trc, JSObject *obj) |
1176 |
{ |
{ |
1177 |
uint32 length; |
uint32 capacity; |
1178 |
size_t i; |
size_t i; |
1179 |
jsval v; |
jsval v; |
1180 |
|
|
1181 |
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); |
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); |
1182 |
|
|
1183 |
length = ARRAY_DENSE_LENGTH(obj); |
capacity = js_DenseArrayCapacity(obj); |
1184 |
for (i = 0; i < length; i++) { |
for (i = 0; i < capacity; i++) { |
1185 |
v = obj->dslots[i]; |
v = obj->dslots[i]; |
1186 |
if (JSVAL_IS_TRACEABLE(v)) { |
if (JSVAL_IS_TRACEABLE(v)) { |
1187 |
JS_SET_TRACING_INDEX(trc, "array_dslots", i); |
JS_SET_TRACING_INDEX(trc, "array_dslots", i); |
1198 |
} |
} |
1199 |
} |
} |
1200 |
|
|
1201 |
static JSObjectMap * |
extern JSObjectOps js_ArrayObjectOps; |
|
array_newObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, |
|
|
JSClass *clasp, JSObject *obj) |
|
|
{ |
|
|
#ifdef DEBUG |
|
|
extern JSClass js_ArrayClass; |
|
|
extern JSObjectOps js_ArrayObjectOps; |
|
|
#endif |
|
|
JSObjectMap *map = (JSObjectMap *) JS_malloc(cx, sizeof(*map)); |
|
|
if (!map) |
|
|
return NULL; |
|
|
|
|
|
map->nrefs = nrefs; |
|
|
JS_ASSERT(ops == &js_ArrayObjectOps); |
|
|
map->ops = ops; |
|
|
JS_ASSERT(clasp == &js_ArrayClass); |
|
|
map->freeslot = JSSLOT_FREE(clasp); |
|
|
|
|
|
return map; |
|
|
} |
|
1202 |
|
|
1203 |
void |
static const JSObjectMap SharedArrayMap = { &js_ArrayObjectOps }; |
|
array_destroyObjectMap(JSContext *cx, JSObjectMap *map) |
|
|
{ |
|
|
JS_free(cx, map); |
|
|
} |
|
1204 |
|
|
1205 |
JSObjectOps js_ArrayObjectOps = { |
JSObjectOps js_ArrayObjectOps = { |
1206 |
array_newObjectMap, array_destroyObjectMap, |
&SharedArrayMap, |
1207 |
array_lookupProperty, array_defineProperty, |
array_lookupProperty, array_defineProperty, |
1208 |
array_getProperty, array_setProperty, |
array_getProperty, array_setProperty, |
1209 |
array_getAttributes, array_setAttributes, |
array_getAttributes, array_setAttributes, |
1211 |
array_enumerate, js_CheckAccess, |
array_enumerate, js_CheckAccess, |
1212 |
NULL, array_dropProperty, |
NULL, array_dropProperty, |
1213 |
NULL, NULL, |
NULL, NULL, |
1214 |
NULL, js_HasInstance, |
js_HasInstance, array_trace, |
1215 |
js_SetProtoOrParent, js_SetProtoOrParent, |
NULL, NULL, |
1216 |
array_trace, NULL, |
NULL |
|
NULL, NULL |
|
1217 |
}; |
}; |
1218 |
|
|
1219 |
static JSObjectOps * |
static JSObjectOps * |
1247 |
JSBool |
JSBool |
1248 |
js_MakeArraySlow(JSContext *cx, JSObject *obj) |
js_MakeArraySlow(JSContext *cx, JSObject *obj) |
1249 |
{ |
{ |
|
JSObjectMap *map, *oldmap; |
|
|
uint32 i, length; |
|
|
|
|
1250 |
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass); |
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass); |
1251 |
|
|
1252 |
/* Create a native scope. */ |
/* Create a native scope. */ |
1253 |
map = js_NewObjectMap(cx, obj->map->nrefs, &js_SlowArrayObjectOps, |
JSScope *scope = js_NewScope(cx, &js_SlowArrayObjectOps, |
1254 |
&js_SlowArrayClass, obj); |
&js_SlowArrayClass, obj); |
1255 |
if (!map) |
if (!scope) |
1256 |
return JS_FALSE; |
return JS_FALSE; |
1257 |
|
|
1258 |
length = ARRAY_DENSE_LENGTH(obj); |
uint32 capacity = js_DenseArrayCapacity(obj); |
1259 |
if (length) { |
if (capacity) { |
1260 |
map->freeslot = STOBJ_NSLOTS(obj) + JS_INITIAL_NSLOTS; |
scope->freeslot = STOBJ_NSLOTS(obj) + JS_INITIAL_NSLOTS; |
1261 |
obj->dslots[-1] = JS_INITIAL_NSLOTS + length; |
obj->dslots[-1] = JS_INITIAL_NSLOTS + capacity; |
1262 |
} else { |
} else { |
1263 |
map->freeslot = STOBJ_NSLOTS(obj); |
scope->freeslot = STOBJ_NSLOTS(obj); |
1264 |
} |
} |
1265 |
|
|
1266 |
/* Create new properties pointing to existing values in dslots */ |
/* Create new properties pointing to existing values in dslots */ |
1267 |
for (i = 0; i < length; i++) { |
for (uint32 i = 0; i < capacity; i++) { |
1268 |
jsid id; |
jsid id; |
1269 |
JSScopeProperty *sprop; |
JSScopeProperty *sprop; |
1270 |
|
|
1276 |
continue; |
continue; |
1277 |
} |
} |
1278 |
|
|
1279 |
sprop = js_AddScopeProperty(cx, (JSScope *)map, id, NULL, NULL, |
sprop = js_AddScopeProperty(cx, scope, id, NULL, NULL, |
1280 |
i + JS_INITIAL_NSLOTS, JSPROP_ENUMERATE, |
i + JS_INITIAL_NSLOTS, JSPROP_ENUMERATE, |
1281 |
0, 0); |
0, 0); |
1282 |
if (!sprop) |
if (!sprop) |
1289 |
* we can tell when only named properties have been added to a dense array |
* we can tell when only named properties have been added to a dense array |
1290 |
* to make it slow-but-not-sparse. |
* to make it slow-but-not-sparse. |
1291 |
*/ |
*/ |
1292 |
length = obj->fslots[JSSLOT_ARRAY_LENGTH]; |
{ |
1293 |
obj->fslots[JSSLOT_ARRAY_COUNT] = INT_FITS_IN_JSVAL(length) |
uint32 length = obj->fslots[JSSLOT_ARRAY_LENGTH]; |
1294 |
? INT_TO_JSVAL(length) |
obj->fslots[JSSLOT_ARRAY_COUNT] = INT_FITS_IN_JSVAL(length) |
1295 |
: JSVAL_VOID; |
? INT_TO_JSVAL(length) |
1296 |
|
: JSVAL_VOID; |
1297 |
|
} |
1298 |
|
|
1299 |
/* Make sure we preserve any flags borrowing bits in classword. */ |
/* Make sure we preserve any flags borrowing bits in classword. */ |
1300 |
obj->classword ^= (jsuword) &js_ArrayClass; |
obj->classword ^= (jsuword) &js_ArrayClass; |
1301 |
obj->classword |= (jsuword) &js_SlowArrayClass; |
obj->classword |= (jsuword) &js_SlowArrayClass; |
1302 |
|
|
1303 |
/* Swap in our new map. */ |
obj->map = &scope->map; |
|
oldmap = obj->map; |
|
|
obj->map = map; |
|
|
array_destroyObjectMap(cx, oldmap); |
|
|
|
|
1304 |
return JS_TRUE; |
return JS_TRUE; |
1305 |
|
|
1306 |
out_bad: |
out_bad: |
1307 |
js_DestroyObjectMap(cx, map); |
js_DestroyScope(cx, scope); |
1308 |
return JS_FALSE; |
return JS_FALSE; |
1309 |
} |
} |
1310 |
|
|
1345 |
growth = (size_t) -1; |
growth = (size_t) -1; |
1346 |
#endif |
#endif |
1347 |
|
|
1348 |
if (op == TO_SOURCE) { |
/* |
1349 |
if (IS_SHARP(he)) { |
* We must check for the sharp bit and skip js_LeaveSharpObject when it is |
1350 |
|
* set even when op is not TO_SOURCE. A script can overwrite the default |
1351 |
|
* toSource implementation and trigger a call, for example, to the |
1352 |
|
* toString method during serialization of the object graph (bug 369696). |
1353 |
|
*/ |
1354 |
|
if (IS_SHARP(he)) { |
1355 |
#if JS_HAS_SHARP_VARS |
#if JS_HAS_SHARP_VARS |
1356 |
nchars = js_strlen(chars); |
nchars = js_strlen(chars); |
1357 |
#else |
#else |
1358 |
chars[0] = '['; |
chars[0] = '['; |
1359 |
chars[1] = ']'; |
chars[1] = ']'; |
1360 |
chars[2] = 0; |
chars[2] = 0; |
1361 |
nchars = 2; |
nchars = 2; |
1362 |
#endif |
#endif |
1363 |
goto make_string; |
goto make_string; |
1364 |
} |
} |
1365 |
|
|
1366 |
|
if (op == TO_SOURCE) { |
1367 |
/* |
/* |
1368 |
* Always allocate 2 extra chars for closing ']' and terminating 0 |
* Always allocate 2 extra chars for closing ']' and terminating 0 |
1369 |
* and then preallocate 1 + extratail to include starting '['. |
* and then preallocate 1 + extratail to include starting '['. |
1422 |
|
|
1423 |
/* Use rval to locally root each element value as we loop and convert. */ |
/* Use rval to locally root each element value as we loop and convert. */ |
1424 |
for (index = 0; index < length; index++) { |
for (index = 0; index < length; index++) { |
1425 |
ok = (JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) && |
ok = (JS_CHECK_OPERATION_LIMIT(cx) && |
1426 |
GetArrayElement(cx, obj, index, &hole, rval)); |
GetArrayElement(cx, obj, index, &hole, rval)); |
1427 |
if (!ok) |
if (!ok) |
1428 |
goto done; |
goto done; |
1475 |
goto done; |
goto done; |
1476 |
} |
} |
1477 |
growth *= sizeof(jschar); |
growth *= sizeof(jschar); |
|
JS_COUNT_OPERATION(cx, JSOW_ALLOCATION); |
|
1478 |
if (!chars) { |
if (!chars) { |
1479 |
chars = (jschar *) malloc(growth); |
chars = (jschar *) malloc(growth); |
1480 |
if (!chars) |
if (!chars) |
1579 |
return array_join_sub(cx, obj, TO_LOCALE_STRING, NULL, vp); |
return array_join_sub(cx, obj, TO_LOCALE_STRING, NULL, vp); |
1580 |
} |
} |
1581 |
|
|
1582 |
|
enum TargetElementsType { |
1583 |
|
TargetElementsAllHoles, |
1584 |
|
TargetElementsMayContainValues |
1585 |
|
}; |
1586 |
|
|
1587 |
|
enum SourceVectorType { |
1588 |
|
SourceVectorAllValues, |
1589 |
|
SourceVectorMayContainHoles |
1590 |
|
}; |
1591 |
|
|
1592 |
static JSBool |
static JSBool |
1593 |
InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint end, |
InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, jsval *vector, |
1594 |
jsval *vector) |
TargetElementsType targetType, SourceVectorType vectorType) |
1595 |
{ |
{ |
1596 |
if (OBJ_IS_DENSE_ARRAY(cx, obj)) { |
JS_ASSERT(count < MAXINDEX); |
|
if (!EnsureLength(cx, obj, end)) |
|
|
return JS_FALSE; |
|
1597 |
|
|
1598 |
if (end > (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH]) |
/* |
1599 |
obj->fslots[JSSLOT_ARRAY_LENGTH] = end; |
* Optimize for dense arrays so long as adding the given set of elements |
1600 |
|
* wouldn't otherwise make the array slow. |
1601 |
|
*/ |
1602 |
|
if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) && |
1603 |
|
start <= MAXINDEX - count && !INDEX_TOO_BIG(start + count)) { |
1604 |
|
|
1605 |
memcpy(obj->dslots + start, vector, sizeof(jsval) * (end - start)); |
#ifdef DEBUG_jwalden |
1606 |
|
{ |
1607 |
|
/* Verify that overwriteType and writeType were accurate. */ |
1608 |
|
JSAutoTempIdRooter idr(cx, JSVAL_ZERO); |
1609 |
|
for (jsuint i = 0; i < count; i++) { |
1610 |
|
JS_ASSERT_IF(vectorType == SourceVectorAllValues, vector[i] != JSVAL_HOLE); |
1611 |
|
|
1612 |
|
jsdouble index = jsdouble(start) + i; |
1613 |
|
if (targetType == TargetElementsAllHoles && index < jsuint(-1)) { |
1614 |
|
JS_ASSERT(ReallyBigIndexToId(cx, index, idr.addr())); |
1615 |
|
JSObject* obj2; |
1616 |
|
JSProperty* prop; |
1617 |
|
JS_ASSERT(OBJ_LOOKUP_PROPERTY(cx, obj, idr.id(), &obj2, &prop)); |
1618 |
|
JS_ASSERT(!prop); |
1619 |
|
} |
1620 |
|
} |
1621 |
|
} |
1622 |
|
#endif |
1623 |
|
|
1624 |
|
jsuint newlen = start + count; |
1625 |
|
JS_ASSERT(jsdouble(start) + count == jsdouble(newlen)); |
1626 |
|
if (!EnsureCapacity(cx, obj, newlen)) |
1627 |
|
return JS_FALSE; |
1628 |
|
|
1629 |
|
if (newlen > uint32(obj->fslots[JSSLOT_ARRAY_LENGTH])) |
1630 |
|
obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen; |
1631 |
|
|
1632 |
|
JS_ASSERT(count < size_t(-1) / sizeof(jsval)); |
1633 |
|
if (targetType == TargetElementsMayContainValues) { |
1634 |
|
jsuint valueCount = 0; |
1635 |
|
for (jsuint i = 0; i < count; i++) { |
1636 |
|
if (obj->dslots[start + i] != JSVAL_HOLE) |
1637 |
|
valueCount++; |
1638 |
|
} |
1639 |
|
JS_ASSERT(uint32(obj->fslots[JSSLOT_ARRAY_COUNT]) >= valueCount); |
1640 |
|
obj->fslots[JSSLOT_ARRAY_COUNT] -= valueCount; |
1641 |
|
} |
1642 |
|
memcpy(obj->dslots + start, vector, sizeof(jsval) * count); |
1643 |
|
if (vectorType == SourceVectorAllValues) { |
1644 |
|
obj->fslots[JSSLOT_ARRAY_COUNT] += count; |
1645 |
|
} else { |
1646 |
|
jsuint valueCount = 0; |
1647 |
|
for (jsuint i = 0; i < count; i++) { |
1648 |
|
if (obj->dslots[start + i] != JSVAL_HOLE) |
1649 |
|
valueCount++; |
1650 |
|
} |
1651 |
|
obj->fslots[JSSLOT_ARRAY_COUNT] += valueCount; |
1652 |
|
} |
1653 |
|
JS_ASSERT_IF(count != 0, obj->dslots[newlen - 1] != JSVAL_HOLE); |
1654 |
return JS_TRUE; |
return JS_TRUE; |
1655 |
} |
} |
1656 |
|
|
1657 |
while (start != end) { |
jsval* end = vector + count; |
1658 |
if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) || |
while (vector != end && start < MAXINDEX) { |
1659 |
|
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
1660 |
!SetArrayElement(cx, obj, start++, *vector++)) { |
!SetArrayElement(cx, obj, start++, *vector++)) { |
1661 |
return JS_FALSE; |
return JS_FALSE; |
1662 |
} |
} |
1663 |
} |
} |
1664 |
|
|
1665 |
|
if (vector == end) |
1666 |
|
return JS_TRUE; |
1667 |
|
|
1668 |
|
/* Finish out any remaining elements past the max array index. */ |
1669 |
|
if (OBJ_IS_DENSE_ARRAY(cx, obj) && !ENSURE_SLOW_ARRAY(cx, obj)) |
1670 |
|
return JS_FALSE; |
1671 |
|
|
1672 |
|
JS_ASSERT(start == MAXINDEX); |
1673 |
|
jsval tmp[2] = {JSVAL_NULL, JSVAL_NULL}; |
1674 |
|
jsdouble* dp = js_NewWeaklyRootedDouble(cx, MAXINDEX); |
1675 |
|
if (!dp) |
1676 |
|
return JS_FALSE; |
1677 |
|
tmp[0] = DOUBLE_TO_JSVAL(dp); |
1678 |
|
JSAutoTempValueRooter(cx, JS_ARRAY_LENGTH(tmp), tmp); |
1679 |
|
JSAutoTempIdRooter idr(cx); |
1680 |
|
do { |
1681 |
|
tmp[1] = *vector++; |
1682 |
|
if (!js_ValueToStringId(cx, tmp[0], idr.addr()) || |
1683 |
|
!OBJ_SET_PROPERTY(cx, obj, idr.id(), &tmp[1])) { |
1684 |
|
return JS_FALSE; |
1685 |
|
} |
1686 |
|
*dp += 1; |
1687 |
|
} while (vector != end); |
1688 |
|
|
1689 |
return JS_TRUE; |
return JS_TRUE; |
1690 |
} |
} |
1691 |
|
|
1698 |
obj->fslots[JSSLOT_ARRAY_LENGTH] = length; |
obj->fslots[JSSLOT_ARRAY_LENGTH] = length; |
1699 |
|
|
1700 |
if (vector) { |
if (vector) { |
1701 |
if (!EnsureLength(cx, obj, length)) |
if (!EnsureCapacity(cx, obj, length)) |
1702 |
return JS_FALSE; |
return JS_FALSE; |
1703 |
|
|
1704 |
jsuint count = length; |
jsuint count = length; |
1722 |
static JSString* FASTCALL |
static JSString* FASTCALL |
1723 |
Array_p_join(JSContext* cx, JSObject* obj, JSString *str) |
Array_p_join(JSContext* cx, JSObject* obj, JSString *str) |
1724 |
{ |
{ |
1725 |
jsval v; |
JSAutoTempValueRooter tvr(cx); |
1726 |
if (!array_join_sub(cx, obj, TO_STRING, str, &v)) |
if (!array_join_sub(cx, obj, TO_STRING, str, tvr.addr())) { |
1727 |
|
js_SetBuiltinError(cx); |
1728 |
return NULL; |
return NULL; |
1729 |
JS_ASSERT(JSVAL_IS_STRING(v)); |
} |
1730 |
return JSVAL_TO_STRING(v); |
return JSVAL_TO_STRING(tvr.value()); |
1731 |
} |
} |
1732 |
|
|
1733 |
static JSString* FASTCALL |
static JSString* FASTCALL |
1734 |
Array_p_toString(JSContext* cx, JSObject* obj) |
Array_p_toString(JSContext* cx, JSObject* obj) |
1735 |
{ |
{ |
1736 |
jsval v; |
JSAutoTempValueRooter tvr(cx); |
1737 |
if (!array_join_sub(cx, obj, TO_STRING, NULL, &v)) |
if (!array_join_sub(cx, obj, TO_STRING, NULL, tvr.addr())) { |
1738 |
|
js_SetBuiltinError(cx); |
1739 |
return NULL; |
return NULL; |
1740 |
JS_ASSERT(JSVAL_IS_STRING(v)); |
} |
1741 |
return JSVAL_TO_STRING(v); |
return JSVAL_TO_STRING(tvr.value()); |
1742 |
} |
} |
1743 |
#endif |
#endif |
1744 |
|
|
1774 |
obj = JS_THIS_OBJECT(cx, vp); |
obj = JS_THIS_OBJECT(cx, vp); |
1775 |
if (!obj || !js_GetLengthProperty(cx, obj, &len)) |
if (!obj || !js_GetLengthProperty(cx, obj, &len)) |
1776 |
return JS_FALSE; |
return JS_FALSE; |
1777 |
|
*vp = OBJECT_TO_JSVAL(obj); |
1778 |
|
|
1779 |
|
if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj)) { |
1780 |
|
/* An empty array or an array with no elements is already reversed. */ |
1781 |
|
if (len == 0 || !obj->dslots) |
1782 |
|
return JS_TRUE; |
1783 |
|
|
1784 |
|
/* |
1785 |
|
* It's actually surprisingly complicated to reverse an array due to the |
1786 |
|
* orthogonality of array length and array capacity while handling |
1787 |
|
* leading and trailing holes correctly. Reversing seems less likely to |
1788 |
|
* be a common operation than other array mass-mutation methods, so for |
1789 |
|
* now just take a probably-small memory hit (in the absence of too many |
1790 |
|
* holes in the array at its start) and ensure that the capacity is |
1791 |
|
* sufficient to hold all the elements in the array if it were full. |
1792 |
|
*/ |
1793 |
|
if (!EnsureCapacity(cx, obj, len)) |
1794 |
|
return JS_FALSE; |
1795 |
|
|
1796 |
|
jsval* lo = &obj->dslots[0]; |
1797 |
|
jsval* hi = &obj->dslots[len - 1]; |
1798 |
|
for (; lo < hi; lo++, hi--) { |
1799 |
|
jsval tmp = *lo; |
1800 |
|
*lo = *hi; |
1801 |
|
*hi = tmp; |
1802 |
|
} |
1803 |
|
|
1804 |
|
/* |
1805 |
|
* Per ECMA-262, don't update the length of the array, even if the new |
1806 |
|
* array has trailing holes (and thus the original array began with |
1807 |
|
* holes). |
1808 |
|
*/ |
1809 |
|
return JS_TRUE; |
1810 |
|
} |
1811 |
|
|
1812 |
ok = JS_TRUE; |
ok = JS_TRUE; |
1813 |
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); |
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); |
1814 |
half = len / 2; |
half = len / 2; |
1815 |
for (i = 0; i < half; i++) { |
for (i = 0; i < half; i++) { |
1816 |
ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) && |
ok = JS_CHECK_OPERATION_LIMIT(cx) && |
1817 |
GetArrayElement(cx, obj, i, &hole, &tvr.u.value) && |
GetArrayElement(cx, obj, i, &hole, &tvr.u.value) && |
1818 |
GetArrayElement(cx, obj, len - i - 1, &hole2, vp) && |
GetArrayElement(cx, obj, len - i - 1, &hole2, vp) && |
1819 |
SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.u.value) && |
SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.u.value) && |
1835 |
} MSortArgs; |
} MSortArgs; |
1836 |
|
|
1837 |
/* Helper function for js_MergeSort. */ |
/* Helper function for js_MergeSort. */ |
1838 |
static JSBool |
static JS_REQUIRES_STACK JSBool |
1839 |
MergeArrays(MSortArgs *msa, void *src, void *dest, size_t run1, size_t run2) |
MergeArrays(MSortArgs *msa, void *src, void *dest, size_t run1, size_t run2) |
1840 |
{ |
{ |
1841 |
void *arg, *a, *b, *c; |
void *arg, *a, *b, *c; |
1896 |
* This sort is stable, i.e. sequence of equal elements is preserved. |
* This sort is stable, i.e. sequence of equal elements is preserved. |
1897 |
* See also bug #224128. |
* See also bug #224128. |
1898 |
*/ |
*/ |
1899 |
JSBool |
JS_REQUIRES_STACK JSBool |
1900 |
js_MergeSort(void *src, size_t nel, size_t elsize, |
js_MergeSort(void *src, size_t nel, size_t elsize, |
1901 |
JSComparator cmp, void *arg, void *tmp) |
JSComparator cmp, void *arg, void *tmp) |
1902 |
{ |
{ |
1982 |
jsval *elemroot; /* stack needed for js_Invoke */ |
jsval *elemroot; /* stack needed for js_Invoke */ |
1983 |
} CompareArgs; |
} CompareArgs; |
1984 |
|
|
1985 |
static JSBool |
static JS_REQUIRES_STACK JSBool |
1986 |
sort_compare(void *arg, const void *a, const void *b, int *result) |
sort_compare(void *arg, const void *a, const void *b, int *result) |
1987 |
{ |
{ |
1988 |
jsval av = *(const jsval *)a, bv = *(const jsval *)b; |
jsval av = *(const jsval *)a, bv = *(const jsval *)b; |
1998 |
JS_ASSERT(!JSVAL_IS_VOID(av)); |
JS_ASSERT(!JSVAL_IS_VOID(av)); |
1999 |
JS_ASSERT(!JSVAL_IS_VOID(bv)); |
JS_ASSERT(!JSVAL_IS_VOID(bv)); |
2000 |
|
|
2001 |
if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP)) |
if (!JS_CHECK_OPERATION_LIMIT(cx)) |
2002 |
return JS_FALSE; |
return JS_FALSE; |
2003 |
|
|
2004 |
invokevp = ca->elemroot; |
invokevp = ca->elemroot; |
2036 |
|
|
2037 |
JS_ASSERT(JSVAL_IS_STRING(av)); |
JS_ASSERT(JSVAL_IS_STRING(av)); |
2038 |
JS_ASSERT(JSVAL_IS_STRING(bv)); |
JS_ASSERT(JSVAL_IS_STRING(bv)); |
2039 |
if (!JS_CHECK_OPERATION_LIMIT((JSContext *)arg, JSOW_JUMP)) |
if (!JS_CHECK_OPERATION_LIMIT((JSContext *)arg)) |
2040 |
return JS_FALSE; |
return JS_FALSE; |
2041 |
|
|
2042 |
*result = (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv)); |
*result = (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv)); |
2050 |
*/ |
*/ |
2051 |
JS_STATIC_ASSERT(JSVAL_NULL == 0); |
JS_STATIC_ASSERT(JSVAL_NULL == 0); |
2052 |
|
|
2053 |
static JSBool |
static JS_REQUIRES_STACK JSBool |
2054 |
array_sort(JSContext *cx, uintN argc, jsval *vp) |
array_sort(JSContext *cx, uintN argc, jsval *vp) |
2055 |
{ |
{ |
2056 |
jsval *argv, fval, *vec, *mergesort_tmp, v; |
jsval *argv, fval, *vec, *mergesort_tmp, v; |
2059 |
jsuint len, newlen, i, undefs; |
jsuint len, newlen, i, undefs; |
2060 |
JSTempValueRooter tvr; |
JSTempValueRooter tvr; |
2061 |
JSBool hole; |
JSBool hole; |
2062 |
bool ok; |
JSBool ok; |
2063 |
size_t elemsize; |
size_t elemsize; |
2064 |
JSString *str; |
JSString *str; |
2065 |
|
|
2130 |
newlen = 0; |
newlen = 0; |
2131 |
all_strings = JS_TRUE; |
all_strings = JS_TRUE; |
2132 |
for (i = 0; i < len; i++) { |
for (i = 0; i < len; i++) { |
2133 |
ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP); |
ok = JS_CHECK_OPERATION_LIMIT(cx); |
2134 |
if (!ok) |
if (!ok) |
2135 |
goto out; |
goto out; |
2136 |
|
|
2216 |
i = newlen; |
i = newlen; |
2217 |
do { |
do { |
2218 |
--i; |
--i; |
2219 |
ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP); |
ok = JS_CHECK_OPERATION_LIMIT(cx); |
2220 |
if (!ok) |
if (!ok) |
2221 |
goto out; |
goto out; |
2222 |
v = vec[i]; |
v = vec[i]; |
2282 |
* easier. |
* easier. |
2283 |
*/ |
*/ |
2284 |
tvr.count = newlen; |
tvr.count = newlen; |
2285 |
ok = InitArrayElements(cx, obj, 0, newlen, vec); |
ok = InitArrayElements(cx, obj, 0, newlen, vec, TargetElementsMayContainValues, |
2286 |
|
SourceVectorAllValues); |
2287 |
if (!ok) |
if (!ok) |
2288 |
goto out; |
goto out; |
2289 |
|
|
2296 |
/* Set undefs that sorted after the rest of elements. */ |
/* Set undefs that sorted after the rest of elements. */ |
2297 |
while (undefs != 0) { |
while (undefs != 0) { |
2298 |
--undefs; |
--undefs; |
2299 |
if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) || |
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
2300 |
!SetArrayElement(cx, obj, newlen++, JSVAL_VOID)) { |
!SetArrayElement(cx, obj, newlen++, JSVAL_VOID)) { |
2301 |
return JS_FALSE; |
return JS_FALSE; |
2302 |
} |
} |
2304 |
|
|
2305 |
/* Re-create any holes that sorted to the end of the array. */ |
/* Re-create any holes that sorted to the end of the array. */ |
2306 |
while (len > newlen) { |
while (len > newlen) { |
2307 |
if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) || |
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
2308 |
!DeleteArrayElement(cx, obj, --len)) { |
!DeleteArrayElement(cx, obj, --len)) { |
2309 |
return JS_FALSE; |
return JS_FALSE; |
2310 |
} |
} |
2319 |
static JSBool |
static JSBool |
2320 |
array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
2321 |
{ |
{ |
2322 |
jsuint length, newlength; |
jsuint length; |
2323 |
|
|
2324 |
if (!js_GetLengthProperty(cx, obj, &length)) |
if (!js_GetLengthProperty(cx, obj, &length)) |
2325 |
return JS_FALSE; |
return JS_FALSE; |
2326 |
newlength = length + argc; |
if (!InitArrayElements(cx, obj, length, argc, argv, TargetElementsMayContainValues, |
2327 |
if (!InitArrayElements(cx, obj, length, newlength, argv)) |
SourceVectorAllValues)) { |
2328 |
return JS_FALSE; |
return JS_FALSE; |
2329 |
|
} |
2330 |
|
|
2331 |
/* Per ECMA-262, return the new array length. */ |
/* Per ECMA-262, return the new array length. */ |
2332 |
|
jsdouble newlength = length + jsdouble(argc); |
2333 |
if (!IndexToValue(cx, newlength, rval)) |
if (!IndexToValue(cx, newlength, rval)) |
2334 |
return JS_FALSE; |
return JS_FALSE; |
2335 |
return js_SetLengthProperty(cx, obj, newlength); |
return js_SetLengthProperty(cx, obj, newlength); |
2345 |
return array_push_slowly(cx, obj, 1, &v, rval); |
return array_push_slowly(cx, obj, 1, &v, rval); |
2346 |
} |
} |
2347 |
|
|
2348 |
if (!EnsureLength(cx, obj, length + 1)) |
if (!EnsureCapacity(cx, obj, length + 1)) |
2349 |
return JS_FALSE; |
return JS_FALSE; |
2350 |
obj->fslots[JSSLOT_ARRAY_LENGTH] = length + 1; |
obj->fslots[JSSLOT_ARRAY_LENGTH] = length + 1; |
2351 |
|
|
2355 |
return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], rval); |
return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], rval); |
2356 |
} |
} |
2357 |
|
|
2358 |
|
JSBool JS_FASTCALL |
2359 |
|
js_ArrayCompPush(JSContext *cx, JSObject *obj, jsval v) |
2360 |
|
{ |
2361 |
|
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); |
2362 |
|
uint32_t length = (uint32_t) obj->fslots[JSSLOT_ARRAY_LENGTH]; |
2363 |
|
JS_ASSERT(length <= js_DenseArrayCapacity(obj)); |
2364 |
|
|
2365 |
|
if (length == js_DenseArrayCapacity(obj)) { |
2366 |
|
if (length >= ARRAY_INIT_LIMIT) { |
2367 |
|
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, |
2368 |
|
JSMSG_ARRAY_INIT_TOO_BIG); |
2369 |
|
return JS_FALSE; |
2370 |
|
} |
2371 |
|
|
2372 |
|
if (!EnsureCapacity(cx, obj, length + 1)) |
2373 |
|
return JS_FALSE; |
2374 |
|
} |
2375 |
|
obj->fslots[JSSLOT_ARRAY_LENGTH] = length + 1; |
2376 |
|
obj->fslots[JSSLOT_ARRAY_COUNT]++; |
2377 |
|
obj->dslots[length] = v; |
2378 |
|
return JS_TRUE; |
2379 |
|
} |
2380 |
|
|
2381 |
#ifdef JS_TRACER |
#ifdef JS_TRACER |
2382 |
static jsval FASTCALL |
static jsval FASTCALL |
2383 |
Array_p_push1(JSContext* cx, JSObject* obj, jsval v) |
Array_p_push1(JSContext* cx, JSObject* obj, jsval v) |
2384 |
{ |
{ |
2385 |
if (OBJ_IS_DENSE_ARRAY(cx, obj) |
JSAutoTempValueRooter tvr(cx, v); |
2386 |
? array_push1_dense(cx, obj, v, &v) |
if (OBJ_IS_DENSE_ARRAY(cx, obj) |
2387 |
: array_push_slowly(cx, obj, 1, &v, &v)) { |
? array_push1_dense(cx, obj, v, tvr.addr()) |
2388 |
return v; |
: array_push_slowly(cx, obj, 1, tvr.addr(), tvr.addr())) { |
2389 |
|
return tvr.value(); |
2390 |
} |
} |
2391 |
return JSVAL_ERROR_COOKIE; |
js_SetBuiltinError(cx); |
2392 |
|
return JSVAL_VOID; |
2393 |
} |
} |
2394 |
#endif |
#endif |
2395 |
|
|
2448 |
return JS_FALSE; |
return JS_FALSE; |
2449 |
obj->fslots[JSSLOT_ARRAY_LENGTH] = index; |
obj->fslots[JSSLOT_ARRAY_LENGTH] = index; |
2450 |
return JS_TRUE; |
return JS_TRUE; |
|
|
|
2451 |
} |
} |
2452 |
|
|
2453 |
#ifdef JS_TRACER |
#ifdef JS_TRACER |
2454 |
static jsval FASTCALL |
static jsval FASTCALL |
2455 |
Array_p_pop(JSContext* cx, JSObject* obj) |
Array_p_pop(JSContext* cx, JSObject* obj) |
2456 |
{ |
{ |
2457 |
jsval v; |
JSAutoTempValueRooter tvr(cx); |
2458 |
if (OBJ_IS_DENSE_ARRAY(cx, obj) |
if (OBJ_IS_DENSE_ARRAY(cx, obj) |
2459 |
? array_pop_dense(cx, obj, &v) |
? array_pop_dense(cx, obj, tvr.addr()) |
2460 |
: array_pop_slowly(cx, obj, &v)) { |
: array_pop_slowly(cx, obj, tvr.addr())) { |
2461 |
return v; |
return tvr.value(); |
2462 |
} |
} |
2463 |
return JSVAL_ERROR_COOKIE; |
js_SetBuiltinError(cx); |
2464 |
|
return JSVAL_VOID; |
2465 |
} |
} |
2466 |
#endif |
#endif |
2467 |
|
|
2473 |
obj = JS_THIS_OBJECT(cx, vp); |
obj = JS_THIS_OBJECT(cx, vp); |
2474 |
if (!obj) |
if (!obj) |
2475 |
return JS_FALSE; |
return JS_FALSE; |
2476 |
if (OBJ_IS_DENSE_ARRAY(cx, obj)) |
if (OBJ_IS_DENSE_ARRAY(cx, obj)) |
2477 |
return array_pop_dense(cx, obj, vp); |
return array_pop_dense(cx, obj, vp); |
2478 |
return array_pop_slowly(cx, obj, vp); |
return array_pop_slowly(cx, obj, vp); |
2479 |
} |
} |
2483 |
{ |
{ |
2484 |
JSObject *obj; |
JSObject *obj; |
2485 |
jsuint length, i; |
jsuint length, i; |
2486 |
JSBool hole, ok; |
JSBool hole; |
|
JSTempValueRooter tvr; |
|
2487 |
|
|
2488 |
obj = JS_THIS_OBJECT(cx, vp); |
obj = JS_THIS_OBJECT(cx, vp); |
2489 |
if (!obj || !js_GetLengthProperty(cx, obj, &length)) |
if (!obj || !js_GetLengthProperty(cx, obj, &length)) |
2493 |
} else { |
} else { |
2494 |
length--; |
length--; |
2495 |
|
|
2496 |
/* Get the to-be-deleted property's value into vp ASAP. */ |
if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) && |
2497 |
if (!GetArrayElement(cx, obj, 0, &hole, vp)) |
length < js_DenseArrayCapacity(obj)) { |
2498 |
return JS_FALSE; |
if (JS_LIKELY(obj->dslots != NULL)) { |
2499 |
|
*vp = obj->dslots[0]; |
2500 |
|
if (*vp == JSVAL_HOLE) |
2501 |
|
*vp = JSVAL_VOID; |
2502 |
|
else |
2503 |
|
obj->fslots[JSSLOT_ARRAY_COUNT]--; |
2504 |
|
memmove(obj->dslots, obj->dslots + 1, length * sizeof(jsval)); |
2505 |
|
obj->dslots[length] = JSVAL_HOLE; |
2506 |
|
} else { |
2507 |
|
/* |
2508 |
|
* We don't need to modify the indexed properties of an empty array |
2509 |
|
* with an explicitly set non-zero length when shift() is called on |
2510 |
|
* it, but note fallthrough to reduce the length by one. |
2511 |
|
*/ |
2512 |
|
JS_ASSERT(obj->fslots[JSSLOT_ARRAY_COUNT] == 0); |
2513 |
|
*vp = JSVAL_VOID; |
2514 |
|
} |
2515 |
|
} else { |
2516 |
|
/* Get the to-be-deleted property's value into vp ASAP. */ |
2517 |
|
if (!GetArrayElement(cx, obj, 0, &hole, vp)) |
2518 |
|
return JS_FALSE; |
2519 |
|
|
2520 |
/* Slide down the array above the first element. */ |
/* Slide down the array above the first element. */ |
2521 |
ok = JS_TRUE; |
JSAutoTempValueRooter tvr(cx, JSVAL_NULL); |
2522 |
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); |
for (i = 0; i != length; i++) { |
2523 |
for (i = 0; i != length; i++) { |
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
2524 |
ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) && |
!GetArrayElement(cx, obj, i + 1, &hole, tvr.addr()) || |
2525 |
GetArrayElement(cx, obj, i + 1, &hole, &tvr.u.value) && |
!SetOrDeleteArrayElement(cx, obj, i, hole, tvr.value())) { |
2526 |
SetOrDeleteArrayElement(cx, obj, i, hole, tvr.u.value); |
return JS_FALSE; |
2527 |
if (!ok) |
} |
2528 |
break; |
} |
|
} |
|
|
JS_POP_TEMP_ROOT(cx, &tvr); |
|
|
if (!ok) |
|
|
return JS_FALSE; |
|
2529 |
|
|
2530 |
/* Delete the only or last element when it exist. */ |
/* Delete the only or last element when it exists. */ |
2531 |
if (!hole && !DeleteArrayElement(cx, obj, length)) |
if (!hole && !DeleteArrayElement(cx, obj, length)) |
2532 |
return JS_FALSE; |
return JS_FALSE; |
2533 |
|
} |
2534 |
} |
} |
2535 |
return js_SetLengthProperty(cx, obj, length); |
return js_SetLengthProperty(cx, obj, length); |
2536 |
} |
} |
2540 |
{ |
{ |
2541 |
JSObject *obj; |
JSObject *obj; |
2542 |
jsval *argv; |
jsval *argv; |
2543 |
jsuint length, last; |
jsuint length; |
2544 |
JSBool hole, ok; |
JSBool hole; |
2545 |
JSTempValueRooter tvr; |
jsdouble last, newlen; |
2546 |
|
|
2547 |
obj = JS_THIS_OBJECT(cx, vp); |
obj = JS_THIS_OBJECT(cx, vp); |
2548 |
if (!obj || !js_GetLengthProperty(cx, obj, &length)) |
if (!obj || !js_GetLengthProperty(cx, obj, &length)) |
2549 |
return JS_FALSE; |
return JS_FALSE; |
2550 |
|
newlen = length; |
2551 |
if (argc > 0) { |
if (argc > 0) { |
2552 |
/* Slide up the array to make room for argc at the bottom. */ |
/* Slide up the array to make room for argc at the bottom. */ |
2553 |
argv = JS_ARGV(cx, vp); |
argv = JS_ARGV(cx, vp); |
2554 |
if (length > 0) { |
if (length > 0) { |
2555 |
last = length; |
if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) && |
2556 |
ok = JS_TRUE; |
!INDEX_TOO_SPARSE(obj, newlen + argc)) { |
2557 |
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); |
JS_ASSERT(newlen + argc == length + argc); |
2558 |
do { |
if (!EnsureCapacity(cx, obj, length + argc)) |
2559 |
--last; |
return JS_FALSE; |
2560 |
ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) && |
memmove(obj->dslots + argc, obj->dslots, length * sizeof(jsval)); |
2561 |
GetArrayElement(cx, obj, last, &hole, &tvr.u.value) && |
for (uint32 i = 0; i < argc; i++) |
2562 |
SetOrDeleteArrayElement(cx, obj, last + argc, hole, |
obj->dslots[i] = JSVAL_HOLE; |
2563 |
tvr.u.value); |
} else { |
2564 |
if (!ok) |
last = length; |
2565 |
break; |
jsdouble upperIndex = last + argc; |
2566 |
} while (last != 0); |
JSAutoTempValueRooter tvr(cx, JSVAL_NULL); |
2567 |
JS_POP_TEMP_ROOT(cx, &tvr); |
do { |
2568 |
if (!ok) |
--last, --upperIndex; |
2569 |
return JS_FALSE; |
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
2570 |
|
!GetArrayElement(cx, obj, last, &hole, tvr.addr()) || |
2571 |
|
!SetOrDeleteArrayElement(cx, obj, upperIndex, hole, tvr.value())) { |
2572 |
|
return JS_FALSE; |
2573 |
|
} |
2574 |
|
} while (last != 0); |
2575 |
|
} |
2576 |
} |
} |
2577 |
|
|
2578 |
/* Copy from argv to the bottom of the array. */ |
/* Copy from argv to the bottom of the array. */ |
2579 |
if (!InitArrayElements(cx, obj, 0, argc, argv)) |
if (!InitArrayElements(cx, obj, 0, argc, argv, TargetElementsAllHoles, SourceVectorAllValues)) |
2580 |
return JS_FALSE; |
return JS_FALSE; |
2581 |
|
|
2582 |
length += argc; |
newlen += argc; |
2583 |
if (!js_SetLengthProperty(cx, obj, length)) |
if (!js_SetLengthProperty(cx, obj, newlen)) |
2584 |
return JS_FALSE; |
return JS_FALSE; |
2585 |
} |
} |
2586 |
|
|
2587 |
/* Follow Perl by returning the new array length. */ |
/* Follow Perl by returning the new array length. */ |
2588 |
return IndexToValue(cx, length, vp); |
return IndexToValue(cx, newlen, vp); |
2589 |
} |
} |
2590 |
|
|
2591 |
static JSBool |
static JSBool |
2595 |
JSObject *obj; |
JSObject *obj; |
2596 |
jsuint length, begin, end, count, delta, last; |
jsuint length, begin, end, count, delta, last; |
2597 |
jsdouble d; |
jsdouble d; |
2598 |
JSBool hole, ok; |
JSBool hole; |
2599 |
JSObject *obj2; |
JSObject *obj2; |
|
JSTempValueRooter tvr; |
|
2600 |
|
|
2601 |
/* |
/* |
2602 |
* Create a new array value to return. Our ECMA v2 proposal specs |
* Create a new array value to return. Our ECMA v2 proposal specs |
2653 |
argv++; |
argv++; |
2654 |
} |
} |
2655 |
|
|
2656 |
MUST_FLOW_THROUGH("out"); |
JSAutoTempValueRooter tvr(cx, JSVAL_NULL); |
|
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); |
|
2657 |
|
|
2658 |
/* If there are elements to remove, put them into the return value. */ |
/* If there are elements to remove, put them into the return value. */ |
2659 |
if (count > 0) { |
if (count > 0) { |
2660 |
for (last = begin; last < end; last++) { |
if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) && |
2661 |
ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) && |
!js_PrototypeHasIndexedProperties(cx, obj2) && |
2662 |
GetArrayElement(cx, obj, last, &hole, &tvr.u.value); |
end <= js_DenseArrayCapacity(obj)) { |
2663 |
if (!ok) |
if (!InitArrayObject(cx, obj2, count, obj->dslots + begin, |
2664 |
goto out; |
obj->fslots[JSSLOT_ARRAY_COUNT] != |
2665 |
|
obj->fslots[JSSLOT_ARRAY_LENGTH])) { |
2666 |
|
return JS_FALSE; |
2667 |
|
} |
2668 |
|
} else { |
2669 |
|
for (last = begin; last < end; last++) { |
2670 |
|
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
2671 |
|
!GetArrayElement(cx, obj, last, &hole, tvr.addr())) { |
2672 |
|
return JS_FALSE; |
2673 |
|
} |
2674 |
|
|
2675 |
/* Copy tvr.u.value to new array unless it's a hole. */ |
/* Copy tvr.value() to the new array unless it's a hole. */ |
2676 |
if (!hole) { |
if (!hole && !SetArrayElement(cx, obj2, last - begin, tvr.value())) |
2677 |
ok = SetArrayElement(cx, obj2, last - begin, tvr.u.value); |
return JS_FALSE; |
|
if (!ok) |
|
|
goto out; |
|
2678 |
} |
} |
|
} |
|
2679 |
|
|
2680 |
ok = js_SetLengthProperty(cx, obj2, end - begin); |
if (!js_SetLengthProperty(cx, obj2, count)) |
2681 |
if (!ok) |
return JS_FALSE; |
2682 |
goto out; |
} |
2683 |
} |
} |
2684 |
|
|
2685 |
/* Find the direction (up or down) to copy and make way for argv. */ |
/* Find the direction (up or down) to copy and make way for argv. */ |
2686 |
if (argc > count) { |
if (argc > count) { |
2687 |
delta = (jsuint)argc - count; |
delta = (jsuint)argc - count; |
2688 |
last = length; |
last = length; |
2689 |
/* (uint) end could be 0, so can't use vanilla >= test */ |
if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) && |
2690 |
while (last-- > end) { |
length <= js_DenseArrayCapacity(obj) && |
2691 |
ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) && |
(length == 0 || obj->dslots[length - 1] != JSVAL_HOLE)) { |
2692 |
GetArrayElement(cx, obj, last, &hole, &tvr.u.value) && |
if (!EnsureCapacity(cx, obj, length + delta)) |
2693 |
SetOrDeleteArrayElement(cx, obj, last + delta, hole, |
return JS_FALSE; |
2694 |
tvr.u.value); |
/* (uint) end could be 0, so we can't use a vanilla >= test. */ |
2695 |
if (!ok) |
while (last-- > end) { |
2696 |
goto out; |
jsval srcval = obj->dslots[last]; |
2697 |
|
jsval* dest = &obj->dslots[last + delta]; |
2698 |
|
if (*dest == JSVAL_HOLE && srcval != JSVAL_HOLE) |
2699 |
|
obj->fslots[JSSLOT_ARRAY_COUNT]++; |
2700 |
|
*dest = srcval; |
2701 |
|
} |
2702 |
|
obj->fslots[JSSLOT_ARRAY_LENGTH] += delta; |
2703 |
|
} else { |
2704 |
|
/* (uint) end could be 0, so we can't use a vanilla >= test. */ |
2705 |
|
while (last-- > end) { |
2706 |
|
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
2707 |
|
!GetArrayElement(cx, obj, last, &hole, tvr.addr()) || |
2708 |
|
!SetOrDeleteArrayElement(cx, obj, last + delta, hole, tvr.value())) { |
2709 |
|
return JS_FALSE; |
2710 |
|
} |
2711 |
|
} |
2712 |
} |
} |
2713 |
length += delta; |
length += delta; |
2714 |
} else if (argc < count) { |
} else if (argc < count) { |
2715 |
delta = count - (jsuint)argc; |
delta = count - (jsuint)argc; |
2716 |
for (last = end; last < length; last++) { |
if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) && |
2717 |
ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) && |
length <= js_DenseArrayCapacity(obj)) { |
2718 |
GetArrayElement(cx, obj, last, &hole, &tvr.u.value) && |
/* (uint) end could be 0, so we can't use a vanilla >= test. */ |
2719 |
SetOrDeleteArrayElement(cx, obj, last - delta, hole, |
for (last = end; last < length; last++) { |
2720 |
tvr.u.value); |
jsval srcval = obj->dslots[last]; |
2721 |
if (!ok) |
jsval* dest = &obj->dslots[last - delta]; |
2722 |
goto out; |
if (*dest == JSVAL_HOLE && srcval != JSVAL_HOLE) |
2723 |
|
obj->fslots[JSSLOT_ARRAY_COUNT]++; |
2724 |
|
*dest = srcval; |
2725 |
|
} |
2726 |
|
} else { |
2727 |
|
for (last = end; last < length; last++) { |
2728 |
|
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
2729 |
|
!GetArrayElement(cx, obj, last, &hole, tvr.addr()) || |
2730 |
|
!SetOrDeleteArrayElement(cx, obj, last - delta, hole, tvr.value())) { |
2731 |
|
return JS_FALSE; |
2732 |
|
} |
2733 |
|
} |
2734 |
} |
} |
2735 |
length -= delta; |
length -= delta; |
2736 |
} |
} |
2737 |
|
|
2738 |
/* Copy from argv into the hole to complete the splice. */ |
/* |
2739 |
ok = InitArrayElements(cx, obj, begin, begin + argc, argv); |
* Copy from argv into the hole to complete the splice, and update length in |
2740 |
if (!ok) |
* case we deleted elements from the end. |
2741 |
goto out; |
*/ |
2742 |
|
return InitArrayElements(cx, obj, begin, argc, argv, TargetElementsMayContainValues, |
2743 |
/* Update length in case we deleted elements from the end. */ |
SourceVectorAllValues) && |
2744 |
ok = js_SetLengthProperty(cx, obj, length); |
js_SetLengthProperty(cx, obj, length); |
|
|
|
|
out: |
|
|
JS_POP_TEMP_ROOT(cx, &tvr); |
|
|
return ok; |
|
2745 |
} |
} |
2746 |
|
|
2747 |
/* |
/* |
2765 |
aobj = JS_THIS_OBJECT(cx, vp); |
aobj = JS_THIS_OBJECT(cx, vp); |
2766 |
if (OBJ_IS_DENSE_ARRAY(cx, aobj)) { |
if (OBJ_IS_DENSE_ARRAY(cx, aobj)) { |
2767 |
/* |
/* |
2768 |
* Clone aobj but pass the minimum of its length and capacity (aka |
* Clone aobj but pass the minimum of its length and capacity, to |
2769 |
* "dense length"), to handle a = [1,2,3]; a.length = 10000 "dense" |
* handle a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In |
2770 |
* cases efficiently. In such a case we'll pass 8 (not 3) due to the |
* such a case we'll pass 8 (not 3) due to ARRAY_CAPACITY_MIN, which |
2771 |
* ARRAY_GROWBY over-allocation policy, which will cause nobj to be |
* will cause nobj to be over-allocated to 16. But in the normal case |
2772 |
* over-allocated to 16. But in the normal case where length is <= |
* where length is <= capacity, nobj and aobj will have the same |
2773 |
* capacity, nobj and aobj will have the same dense length. |
* capacity. |
2774 |
*/ |
*/ |
2775 |
length = aobj->fslots[JSSLOT_ARRAY_LENGTH]; |
length = aobj->fslots[JSSLOT_ARRAY_LENGTH]; |
2776 |
jsuint capacity = ARRAY_DENSE_LENGTH(aobj); |
jsuint capacity = js_DenseArrayCapacity(aobj); |
2777 |
nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->dslots, |
nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->dslots, |
2778 |
aobj->fslots[JSSLOT_ARRAY_COUNT] != |
aobj->fslots[JSSLOT_ARRAY_COUNT] != |
2779 |
(jsval) length); |
(jsval) length); |
2798 |
|
|
2799 |
/* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */ |
/* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */ |
2800 |
for (i = 0; i <= argc; i++) { |
for (i = 0; i <= argc; i++) { |
2801 |
ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP); |
ok = JS_CHECK_OPERATION_LIMIT(cx); |
2802 |
if (!ok) |
if (!ok) |
2803 |
goto out; |
goto out; |
2804 |
v = argv[i]; |
v = argv[i]; |
2819 |
if (!ok) |
if (!ok) |
2820 |
goto out; |
goto out; |
2821 |
for (slot = 0; slot < alength; slot++) { |
for (slot = 0; slot < alength; slot++) { |
2822 |
ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) && |
ok = JS_CHECK_OPERATION_LIMIT(cx) && |
2823 |
GetArrayElement(cx, aobj, slot, &hole, |
GetArrayElement(cx, aobj, slot, &hole, |
2824 |
&tvr.u.value); |
&tvr.u.value); |
2825 |
if (!ok) |
if (!ok) |
2861 |
JSObject *nobj, *obj; |
JSObject *nobj, *obj; |
2862 |
jsuint length, begin, end, slot; |
jsuint length, begin, end, slot; |
2863 |
jsdouble d; |
jsdouble d; |
2864 |
JSBool hole, ok; |
JSBool hole; |
|
JSTempValueRooter tvr; |
|
2865 |
|
|
2866 |
argv = JS_ARGV(cx, vp); |
argv = JS_ARGV(cx, vp); |
2867 |
|
|
2904 |
if (begin > end) |
if (begin > end) |
2905 |
begin = end; |
begin = end; |
2906 |
|
|
2907 |
if (OBJ_IS_DENSE_ARRAY(cx, obj) && end <= ARRAY_DENSE_LENGTH(obj)) { |
if (OBJ_IS_DENSE_ARRAY(cx, obj) && end <= js_DenseArrayCapacity(obj) && |
2908 |
|
!js_PrototypeHasIndexedProperties(cx, obj)) { |
2909 |
nobj = js_NewArrayObject(cx, end - begin, obj->dslots + begin, |
nobj = js_NewArrayObject(cx, end - begin, obj->dslots + begin, |
2910 |
obj->fslots[JSSLOT_ARRAY_COUNT] != |
obj->fslots[JSSLOT_ARRAY_COUNT] != |
2911 |
obj->fslots[JSSLOT_ARRAY_LENGTH]); |
obj->fslots[JSSLOT_ARRAY_LENGTH]); |
2921 |
return JS_FALSE; |
return JS_FALSE; |
2922 |
*vp = OBJECT_TO_JSVAL(nobj); |
*vp = OBJECT_TO_JSVAL(nobj); |
2923 |
|
|
2924 |
MUST_FLOW_THROUGH("out"); |
JSAutoTempValueRooter tvr(cx, JSVAL_NULL); |
|
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); |
|
|
|
|
2925 |
for (slot = begin; slot < end; slot++) { |
for (slot = begin; slot < end; slot++) { |
2926 |
ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) && |
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
2927 |
GetArrayElement(cx, obj, slot, &hole, &tvr.u.value); |
!GetArrayElement(cx, obj, slot, &hole, tvr.addr())) { |
2928 |
if (!ok) |
return JS_FALSE; |
|
goto out; |
|
|
if (!hole) { |
|
|
ok = SetArrayElement(cx, nobj, slot - begin, tvr.u.value); |
|
|
if (!ok) |
|
|
goto out; |
|
2929 |
} |
} |
2930 |
|
if (!hole && !SetArrayElement(cx, nobj, slot - begin, tvr.value())) |
2931 |
|
return JS_FALSE; |
2932 |
} |
} |
|
ok = js_SetLengthProperty(cx, nobj, end - begin); |
|
2933 |
|
|
2934 |
out: |
return js_SetLengthProperty(cx, nobj, end - begin); |
|
JS_POP_TEMP_ROOT(cx, &tvr); |
|
|
return ok; |
|
2935 |
} |
} |
2936 |
|
|
2937 |
#if JS_HAS_ARRAY_EXTRAS |
#if JS_HAS_ARRAY_EXTRAS |
2989 |
} |
} |
2990 |
|
|
2991 |
for (;;) { |
for (;;) { |
2992 |
if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) || |
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
2993 |
!GetArrayElement(cx, obj, (jsuint)i, &hole, vp)) { |
!GetArrayElement(cx, obj, (jsuint)i, &hole, vp)) { |
2994 |
return JS_FALSE; |
return JS_FALSE; |
2995 |
} |
} |
3030 |
|
|
3031 |
#define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT) |
#define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT) |
3032 |
|
|
3033 |
static JSBool |
static JS_REQUIRES_STACK JSBool |
3034 |
array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, jsval *vp) |
array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, jsval *vp) |
3035 |
{ |
{ |
3036 |
JSObject *obj; |
JSObject *obj; |
3138 |
invokevp = elemroot + 1; |
invokevp = elemroot + 1; |
3139 |
|
|
3140 |
for (i = start; i != end; i += step) { |
for (i = start; i != end; i += step) { |
3141 |
ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) && |
ok = JS_CHECK_OPERATION_LIMIT(cx) && |
3142 |
GetArrayElement(cx, obj, i, &hole, elemroot); |
GetArrayElement(cx, obj, i, &hole, elemroot); |
3143 |
if (!ok) |
if (!ok) |
3144 |
goto out; |
goto out; |
3214 |
return ok; |
return ok; |
3215 |
} |
} |
3216 |
|
|
3217 |
static JSBool |
static JS_REQUIRES_STACK JSBool |
3218 |
array_forEach(JSContext *cx, uintN argc, jsval *vp) |
array_forEach(JSContext *cx, uintN argc, jsval *vp) |
3219 |
{ |
{ |
3220 |
return array_extra(cx, FOREACH, argc, vp); |
return array_extra(cx, FOREACH, argc, vp); |
3221 |
} |
} |
3222 |
|
|
3223 |
static JSBool |
static JS_REQUIRES_STACK JSBool |
3224 |
array_map(JSContext *cx, uintN argc, jsval *vp) |
array_map(JSContext *cx, uintN argc, jsval *vp) |
3225 |
{ |
{ |
3226 |
return array_extra(cx, MAP, argc, vp); |
return array_extra(cx, MAP, argc, vp); |
3227 |
} |
} |
3228 |
|
|
3229 |
static JSBool |
static JS_REQUIRES_STACK JSBool |
3230 |
array_reduce(JSContext *cx, uintN argc, jsval *vp) |
array_reduce(JSContext *cx, uintN argc, jsval *vp) |
3231 |
{ |
{ |
3232 |
return array_extra(cx, REDUCE, argc, vp); |
return array_extra(cx, REDUCE, argc, vp); |
3233 |
} |
} |
3234 |
|
|
3235 |
static JSBool |
static JS_REQUIRES_STACK JSBool |
3236 |
array_reduceRight(JSContext *cx, uintN argc, jsval *vp) |
array_reduceRight(JSContext *cx, uintN argc, jsval *vp) |
3237 |
{ |
{ |
3238 |
return array_extra(cx, REDUCE_RIGHT, argc, vp); |
return array_extra(cx, REDUCE_RIGHT, argc, vp); |
3239 |
} |
} |
3240 |
|
|
3241 |
static JSBool |
static JS_REQUIRES_STACK JSBool |
3242 |
array_filter(JSContext *cx, uintN argc, jsval *vp) |
array_filter(JSContext *cx, uintN argc, jsval *vp) |
3243 |
{ |
{ |
3244 |
return array_extra(cx, FILTER, argc, vp); |
return array_extra(cx, FILTER, argc, vp); |
3245 |
} |
} |
3246 |
|
|
3247 |
static JSBool |
static JS_REQUIRES_STACK JSBool |
3248 |
array_some(JSContext *cx, uintN argc, jsval *vp) |
array_some(JSContext *cx, uintN argc, jsval *vp) |
3249 |
{ |
{ |
3250 |
return array_extra(cx, SOME, argc, vp); |
return array_extra(cx, SOME, argc, vp); |
3251 |
} |
} |
3252 |
|
|
3253 |
static JSBool |
static JS_REQUIRES_STACK JSBool |
3254 |
array_every(JSContext *cx, uintN argc, jsval *vp) |
array_every(JSContext *cx, uintN argc, jsval *vp) |
3255 |
{ |
{ |
3256 |
return array_extra(cx, EVERY, argc, vp); |
return array_extra(cx, EVERY, argc, vp); |
3315 |
jsval *vector; |
jsval *vector; |
3316 |
|
|
3317 |
/* If called without new, replace obj with a new Array object. */ |
/* If called without new, replace obj with a new Array object. */ |
3318 |
if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { |
if (!JS_IsConstructing(cx)) { |
3319 |
obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL, 0); |
obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL, 0); |
3320 |
if (!obj) |
if (!obj) |
3321 |
return JS_FALSE; |
return JS_FALSE; |
3346 |
#ifdef JS_TRACER |
#ifdef JS_TRACER |
3347 |
|
|
3348 |
JSObject* FASTCALL |
JSObject* FASTCALL |
3349 |
js_FastNewArray(JSContext* cx, JSObject* proto) |
js_NewEmptyArray(JSContext* cx, JSObject* proto) |
3350 |
{ |
{ |
3351 |
JS_ASSERT(OBJ_IS_ARRAY(cx, proto)); |
JS_ASSERT(OBJ_IS_ARRAY(cx, proto)); |
3352 |
|
|
3355 |
if (!obj) |
if (!obj) |
3356 |
return NULL; |
return NULL; |
3357 |
|
|
3358 |
JSClass* clasp = &js_ArrayClass; |
/* Initialize all fields of JSObject. */ |
3359 |
obj->classword = jsuword(clasp); |
obj->map = const_cast<JSObjectMap *>(&SharedArrayMap); |
3360 |
|
obj->classword = jsuword(&js_ArrayClass); |
3361 |
obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); |
obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); |
3362 |
obj->fslots[JSSLOT_PARENT] = proto->fslots[JSSLOT_PARENT]; |
obj->fslots[JSSLOT_PARENT] = proto->fslots[JSSLOT_PARENT]; |
3363 |
|
|
3365 |
obj->fslots[JSSLOT_ARRAY_COUNT] = 0; |
obj->fslots[JSSLOT_ARRAY_COUNT] = 0; |
3366 |
for (unsigned i = JSSLOT_ARRAY_COUNT + 1; i != JS_INITIAL_NSLOTS; ++i) |
for (unsigned i = JSSLOT_ARRAY_COUNT + 1; i != JS_INITIAL_NSLOTS; ++i) |
3367 |
obj->fslots[i] = JSVAL_VOID; |
obj->fslots[i] = JSVAL_VOID; |
|
|
|
|
JSObjectOps* ops = clasp->getObjectOps(cx, clasp); |
|
|
obj->map = ops->newObjectMap(cx, 1, ops, clasp, obj); |
|
|
if (!obj->map) |
|
|
return NULL; |
|
3368 |
obj->dslots = NULL; |
obj->dslots = NULL; |
3369 |
return obj; |
return obj; |
3370 |
} |
} |
3371 |
|
|
3372 |
JSObject* FASTCALL |
JSObject* FASTCALL |
3373 |
js_Array_1int(JSContext* cx, JSObject* proto, int32 i) |
js_NewUninitializedArray(JSContext* cx, JSObject* proto, uint32 len) |
3374 |
{ |
{ |
3375 |
JS_ASSERT(JS_ON_TRACE(cx)); |
JS_ASSERT(JS_ON_TRACE(cx)); |
3376 |
JSObject* obj = js_FastNewArray(cx, proto); |
JSObject* obj = js_NewEmptyArray(cx, proto); |
3377 |
if (obj) |
if (!obj) |
3378 |
obj->fslots[JSSLOT_ARRAY_LENGTH] = i; |
return NULL; |
3379 |
|
obj->fslots[JSSLOT_ARRAY_LENGTH] = len; |
3380 |
|
if (!ResizeSlots(cx, obj, 0, JS_MAX(len, ARRAY_CAPACITY_MIN))) |
3381 |
|
return NULL; |
3382 |
return obj; |
return obj; |
3383 |
} |
} |
3384 |
|
|
|
#define ARRAY_CTOR_GUTS(exact_len, newslots_code) \ |
|
|
JS_ASSERT(JS_ON_TRACE(cx)); \ |
|
|
JSObject* obj = js_FastNewArray(cx, proto); \ |
|
|
if (obj) { \ |
|
|
const uint32 len = ARRAY_GROWBY; \ |
|
|
jsval* newslots = (jsval*) JS_malloc(cx, sizeof (jsval) * (len + 1)); \ |
|
|
if (newslots) { \ |
|
|
obj->dslots = newslots + 1; \ |
|
|
ARRAY_SET_DENSE_LENGTH(obj, len); \ |
|
|
{newslots_code} \ |
|
|
while (++newslots < obj->dslots + len) \ |
|
|
*newslots = JSVAL_HOLE; \ |
|
|
obj->fslots[JSSLOT_ARRAY_LENGTH] = (exact_len); \ |
|
|
return obj; \ |
|
|
} \ |
|
|
} \ |
|
|
return NULL; |
|
|
|
|
|
JSObject* FASTCALL |
|
|
js_Array_1str(JSContext* cx, JSObject* proto, JSString *str) |
|
|
{ |
|
|
ARRAY_CTOR_GUTS(1, *++newslots = STRING_TO_JSVAL(str);) |
|
|
} |
|
|
|
|
|
JSObject* FASTCALL |
|
|
js_Array_2obj(JSContext* cx, JSObject* proto, JSObject *obj1, JSObject* obj2) |
|
|
{ |
|
|
ARRAY_CTOR_GUTS(2, |
|
|
*++newslots = OBJECT_TO_JSVAL(obj1); |
|
|
*++newslots = OBJECT_TO_JSVAL(obj2);) |
|
|
} |
|
|
|
|
|
JSObject* FASTCALL |
|
|
js_Array_3num(JSContext* cx, JSObject* proto, jsdouble n1, jsdouble n2, jsdouble n3) |
|
|
{ |
|
|
ARRAY_CTOR_GUTS(3, |
|
|
if (!js_NewDoubleInRootedValue(cx, n1, ++newslots)) |
|
|
return NULL; |
|
|
if (!js_NewDoubleInRootedValue(cx, n2, ++newslots)) |
|
|
return NULL; |
|
|
if (!js_NewDoubleInRootedValue(cx, n3, ++newslots)) |
|
|
return NULL;) |
|
|
} |
|
|
|
|
3385 |
#endif /* JS_TRACER */ |
#endif /* JS_TRACER */ |
3386 |
|
|
3387 |
JSObject * |
JSObject * |
3457 |
OBJ_IS_DENSE_ARRAY(cx, array) ? "dense" : "sparse", |
OBJ_IS_DENSE_ARRAY(cx, array) ? "dense" : "sparse", |
3458 |
array->fslots[JSSLOT_ARRAY_LENGTH]); |
array->fslots[JSSLOT_ARRAY_LENGTH]); |
3459 |
if (OBJ_IS_DENSE_ARRAY(cx, array)) { |
if (OBJ_IS_DENSE_ARRAY(cx, array)) { |
3460 |
fprintf(stderr, ", count %lu, denselen %lu", |
fprintf(stderr, ", count %lu, capacity %lu", |
3461 |
array->fslots[JSSLOT_ARRAY_COUNT], |
array->fslots[JSSLOT_ARRAY_COUNT], |
3462 |
ARRAY_DENSE_LENGTH(array)); |
js_DenseArrayCapacity(array)); |
3463 |
} |
} |
3464 |
fputs(")\n", stderr); |
fputs(")\n", stderr); |
3465 |
JS_free(cx, bytes); |
JS_free(cx, bytes); |
3659 |
} |
} |
3660 |
|
|
3661 |
JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem, CONTEXT, OBJECT, INT32, JSVAL, 0, 0) |
JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem, CONTEXT, OBJECT, INT32, JSVAL, 0, 0) |
3662 |
JS_DEFINE_CALLINFO_2(extern, OBJECT, js_FastNewArray, CONTEXT, OBJECT, 0, 0) |
JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, 0, 0) |
3663 |
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_Array_1int, CONTEXT, OBJECT, INT32, 0, 0) |
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewUninitializedArray, CONTEXT, OBJECT, UINT32, 0, 0) |
3664 |
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_Array_1str, CONTEXT, OBJECT, STRING, 0, 0) |
JS_DEFINE_CALLINFO_3(extern, BOOL, js_ArrayCompPush, CONTEXT, OBJECT, JSVAL, 0, 0) |
|
JS_DEFINE_CALLINFO_4(extern, OBJECT, js_Array_2obj, CONTEXT, OBJECT, OBJECT, OBJECT, 0, 0) |
|
|
JS_DEFINE_CALLINFO_5(extern, OBJECT, js_Array_3num, CONTEXT, OBJECT, DOUBLE, DOUBLE, DOUBLE, 0, 0) |
|