1 |
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 |
* vim: set sw=4 ts=8 et tw=78: |
* vim: set sw=4 ts=8 et tw=78: |
3 |
* |
* |
4 |
* ***** BEGIN LICENSE BLOCK ***** |
* ***** BEGIN LICENSE BLOCK ***** |
75 |
* js_SlowArrayClass, but have the same performance characteristics as a dense |
* js_SlowArrayClass, but have the same performance characteristics as a dense |
76 |
* array for slot accesses, at some cost in code complexity. |
* array for slot accesses, at some cost in code complexity. |
77 |
*/ |
*/ |
|
#include "jsstddef.h" |
|
78 |
#include <stdlib.h> |
#include <stdlib.h> |
79 |
#include <string.h> |
#include <string.h> |
80 |
#include "jstypes.h" |
#include "jstypes.h" |
81 |
|
#include "jsstdint.h" |
82 |
#include "jsutil.h" /* Added by JSIFY */ |
#include "jsutil.h" /* Added by JSIFY */ |
83 |
#include "jsapi.h" |
#include "jsapi.h" |
84 |
#include "jsarray.h" |
#include "jsarray.h" |
85 |
#include "jsatom.h" |
#include "jsatom.h" |
86 |
#include "jsbit.h" |
#include "jsbit.h" |
87 |
#include "jsbool.h" |
#include "jsbool.h" |
88 |
|
#include "jstracer.h" |
89 |
#include "jsbuiltins.h" |
#include "jsbuiltins.h" |
90 |
#include "jscntxt.h" |
#include "jscntxt.h" |
91 |
#include "jsversion.h" |
#include "jsversion.h" |
100 |
#include "jsscope.h" |
#include "jsscope.h" |
101 |
#include "jsstr.h" |
#include "jsstr.h" |
102 |
#include "jsstaticcheck.h" |
#include "jsstaticcheck.h" |
103 |
|
#include "jsvector.h" |
104 |
|
|
105 |
|
#include "jsatominlines.h" |
106 |
|
|
107 |
/* 2^32 - 1 as a number and a string */ |
/* 2^32 - 1 as a number and a string */ |
108 |
#define MAXINDEX 4294967295u |
#define MAXINDEX 4294967295u |
163 |
return JS_FALSE; |
return JS_FALSE; |
164 |
|
|
165 |
str = JSVAL_TO_STRING(id); |
str = JSVAL_TO_STRING(id); |
166 |
cp = JSSTRING_CHARS(str); |
cp = str->chars(); |
167 |
if (JS7_ISDEC(*cp) && JSSTRING_LENGTH(str) < sizeof(MAXSTR)) { |
if (JS7_ISDEC(*cp) && str->length() < sizeof(MAXSTR)) { |
168 |
jsuint index = JS7_UNDEC(*cp++); |
jsuint index = JS7_UNDEC(*cp++); |
169 |
jsuint oldIndex = 0; |
jsuint oldIndex = 0; |
170 |
jsuint c = 0; |
jsuint c = 0; |
224 |
JSBool |
JSBool |
225 |
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) |
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) |
226 |
{ |
{ |
|
JSTempValueRooter tvr; |
|
|
jsid id; |
|
|
JSBool ok; |
|
|
jsint i; |
|
|
|
|
227 |
if (OBJ_IS_ARRAY(cx, obj)) { |
if (OBJ_IS_ARRAY(cx, obj)) { |
228 |
*lengthp = obj->fslots[JSSLOT_ARRAY_LENGTH]; |
*lengthp = obj->fslots[JSSLOT_ARRAY_LENGTH]; |
229 |
return JS_TRUE; |
return JS_TRUE; |
230 |
} |
} |
231 |
|
|
232 |
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); |
JSAutoTempValueRooter tvr(cx, JSVAL_NULL); |
233 |
id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); |
if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), tvr.addr())) |
234 |
ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value); |
return JS_FALSE; |
235 |
if (ok) { |
|
236 |
if (JSVAL_IS_INT(tvr.u.value)) { |
if (JSVAL_IS_INT(tvr.value())) { |
237 |
i = JSVAL_TO_INT(tvr.u.value); |
*lengthp = jsuint(jsint(JSVAL_TO_INT(tvr.value()))); /* jsuint cast does ToUint32 */ |
238 |
*lengthp = (jsuint)i; /* jsuint cast does ToUint32 */ |
return JS_TRUE; |
|
} else { |
|
|
*lengthp = js_ValueToECMAUint32(cx, &tvr.u.value); |
|
|
ok = !JSVAL_IS_NULL(tvr.u.value); |
|
|
} |
|
239 |
} |
} |
240 |
JS_POP_TEMP_ROOT(cx, &tvr); |
|
241 |
return ok; |
*lengthp = js_ValueToECMAUint32(cx, tvr.addr()); |
242 |
|
return !JSVAL_IS_NULL(tvr.value()); |
243 |
} |
} |
244 |
|
|
245 |
static JSBool |
static JSBool |
309 |
} |
} |
310 |
|
|
311 |
static JSBool |
static JSBool |
312 |
ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldsize, uint32 size) |
ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldlen, uint32 newlen, |
313 |
|
bool initializeAllSlots = true) |
314 |
{ |
{ |
315 |
jsval *slots, *newslots; |
jsval *slots, *newslots; |
316 |
|
|
317 |
if (size == 0) { |
if (newlen == 0) { |
318 |
if (obj->dslots) { |
if (obj->dslots) { |
319 |
JS_free(cx, obj->dslots - 1); |
cx->free(obj->dslots - 1); |
320 |
obj->dslots = NULL; |
obj->dslots = NULL; |
321 |
} |
} |
322 |
return JS_TRUE; |
return JS_TRUE; |
323 |
} |
} |
324 |
|
|
325 |
/* |
if (newlen > MAX_DSLOTS_LENGTH) { |
|
* MAX_DSLOTS_LENGTH is the maximum net capacity supported. Since we allocate |
|
|
* one additional slot to hold the array length, we have to use >= here. |
|
|
*/ |
|
|
if (size >= MAX_DSLOTS_LENGTH) { |
|
326 |
js_ReportAllocationOverflow(cx); |
js_ReportAllocationOverflow(cx); |
327 |
return JS_FALSE; |
return JS_FALSE; |
328 |
} |
} |
329 |
|
|
330 |
slots = obj->dslots ? obj->dslots - 1 : NULL; |
slots = obj->dslots ? obj->dslots - 1 : NULL; |
331 |
newslots = (jsval *) JS_realloc(cx, slots, (size + 1) * sizeof(jsval)); |
newslots = (jsval *) cx->realloc(slots, (size_t(newlen) + 1) * sizeof(jsval)); |
332 |
if (!newslots) |
if (!newslots) |
333 |
return JS_FALSE; |
return JS_FALSE; |
334 |
|
|
335 |
obj->dslots = newslots + 1; |
obj->dslots = newslots + 1; |
336 |
js_SetDenseArrayCapacity(obj, size); |
js_SetDenseArrayCapacity(obj, newlen); |
337 |
|
|
338 |
for (slots = obj->dslots + oldsize; slots < obj->dslots + size; slots++) |
if (initializeAllSlots) { |
339 |
*slots = JSVAL_HOLE; |
for (slots = obj->dslots + oldlen; slots < obj->dslots + newlen; slots++) |
340 |
|
*slots = JSVAL_HOLE; |
341 |
|
} |
342 |
|
|
343 |
return JS_TRUE; |
return JS_TRUE; |
344 |
} |
} |
359 |
#define CAPACITY_CHUNK (1024 * 1024 / sizeof(jsval)) |
#define CAPACITY_CHUNK (1024 * 1024 / sizeof(jsval)) |
360 |
|
|
361 |
static JSBool |
static JSBool |
362 |
EnsureCapacity(JSContext *cx, JSObject *obj, uint32 capacity) |
EnsureCapacity(JSContext *cx, JSObject *obj, uint32 newcap, |
363 |
|
bool initializeAllSlots = true) |
364 |
{ |
{ |
365 |
uint32 oldsize = js_DenseArrayCapacity(obj); |
uint32 oldcap = js_DenseArrayCapacity(obj); |
366 |
|
|
367 |
if (capacity > oldsize) { |
if (newcap > oldcap) { |
368 |
/* |
/* |
369 |
* If this overflows uint32, capacity is very large. nextsize will end |
* If this overflows uint32, newcap is very large. nextsize will end |
370 |
* up being less than capacity, the code below will thus disregard it, |
* up being less than newcap, the code below will thus disregard it, |
371 |
* and ResizeSlots will fail. |
* and ResizeSlots will fail. |
372 |
* |
* |
373 |
* The way we use dslots[-1] forces a few +1s and -1s here. For |
* The way we use dslots[-1] forces a few +1s and -1s here. For |
374 |
* example, (oldsize * 2 + 1) produces the sequence 7, 15, 31, 63, ... |
* example, (oldcap * 2 + 1) produces the sequence 7, 15, 31, 63, ... |
375 |
* which makes the total allocation size (with dslots[-1]) a power |
* which makes the total allocation size (with dslots[-1]) a power |
376 |
* of two. |
* of two. |
377 |
*/ |
*/ |
378 |
uint32 nextsize = (oldsize <= CAPACITY_DOUBLING_MAX) |
uint32 nextsize = (oldcap <= CAPACITY_DOUBLING_MAX) |
379 |
? oldsize * 2 + 1 |
? oldcap * 2 + 1 |
380 |
: oldsize + (oldsize >> 3); |
: oldcap + (oldcap >> 3); |
381 |
|
|
382 |
capacity = JS_MAX(capacity, nextsize); |
uint32 actualCapacity = JS_MAX(newcap, nextsize); |
383 |
if (capacity >= CAPACITY_CHUNK) |
if (actualCapacity >= CAPACITY_CHUNK) |
384 |
capacity = JS_ROUNDUP(capacity + 1, CAPACITY_CHUNK) - 1; /* -1 for dslots[-1] */ |
actualCapacity = JS_ROUNDUP(actualCapacity + 1, CAPACITY_CHUNK) - 1; /* -1 for dslots[-1] */ |
385 |
else if (capacity < ARRAY_CAPACITY_MIN) |
else if (actualCapacity < ARRAY_CAPACITY_MIN) |
386 |
capacity = ARRAY_CAPACITY_MIN; |
actualCapacity = ARRAY_CAPACITY_MIN; |
387 |
return ResizeSlots(cx, obj, oldsize, capacity); |
if (!ResizeSlots(cx, obj, oldcap, actualCapacity, initializeAllSlots)) |
388 |
|
return JS_FALSE; |
389 |
|
if (!initializeAllSlots) { |
390 |
|
/* |
391 |
|
* Initialize the slots caller didn't actually ask for. |
392 |
|
*/ |
393 |
|
for (jsval *slots = obj->dslots + newcap; |
394 |
|
slots < obj->dslots + actualCapacity; |
395 |
|
slots++) { |
396 |
|
*slots = JSVAL_HOLE; |
397 |
|
} |
398 |
|
} |
399 |
} |
} |
400 |
return JS_TRUE; |
return JS_TRUE; |
401 |
} |
} |
460 |
|
|
461 |
JSObject *obj2; |
JSObject *obj2; |
462 |
JSProperty *prop; |
JSProperty *prop; |
463 |
if (!OBJ_LOOKUP_PROPERTY(cx, obj, idr.id(), &obj2, &prop)) |
if (!obj->lookupProperty(cx, idr.id(), &obj2, &prop)) |
464 |
return JS_FALSE; |
return JS_FALSE; |
465 |
if (!prop) { |
if (!prop) { |
466 |
*hole = JS_TRUE; |
*hole = JS_TRUE; |
467 |
*vp = JSVAL_VOID; |
*vp = JSVAL_VOID; |
468 |
} else { |
} else { |
469 |
OBJ_DROP_PROPERTY(cx, obj2, prop); |
obj2->dropProperty(cx, prop); |
470 |
if (!OBJ_GET_PROPERTY(cx, obj, idr.id(), vp)) |
if (!obj->getProperty(cx, idr.id(), vp)) |
471 |
return JS_FALSE; |
return JS_FALSE; |
472 |
*hole = JS_FALSE; |
*hole = JS_FALSE; |
473 |
} |
} |
509 |
return JS_FALSE; |
return JS_FALSE; |
510 |
JS_ASSERT(!JSVAL_IS_VOID(idr.id())); |
JS_ASSERT(!JSVAL_IS_VOID(idr.id())); |
511 |
|
|
512 |
return OBJ_SET_PROPERTY(cx, obj, idr.id(), &v); |
return obj->setProperty(cx, idr.id(), &v); |
513 |
} |
} |
514 |
|
|
515 |
static JSBool |
static JSBool |
537 |
return JS_TRUE; |
return JS_TRUE; |
538 |
|
|
539 |
jsval junk; |
jsval junk; |
540 |
return OBJ_DELETE_PROPERTY(cx, obj, idr.id(), &junk); |
return obj->deleteProperty(cx, idr.id(), &junk); |
541 |
} |
} |
542 |
|
|
543 |
/* |
/* |
564 |
if (!IndexToValue(cx, length, &v)) |
if (!IndexToValue(cx, length, &v)) |
565 |
return JS_FALSE; |
return JS_FALSE; |
566 |
id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); |
id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); |
567 |
return OBJ_SET_PROPERTY(cx, obj, id, &v); |
return obj->setProperty(cx, id, &v); |
568 |
} |
} |
569 |
|
|
570 |
JSBool |
JSBool |
571 |
js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) |
js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) |
572 |
{ |
{ |
573 |
JSErrorReporter older; |
JSErrorReporter older = JS_SetErrorReporter(cx, NULL); |
574 |
JSTempValueRooter tvr; |
JSAutoTempValueRooter tvr(cx, JSVAL_NULL); |
575 |
jsid id; |
jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); |
576 |
JSBool ok; |
JSBool ok = obj->getProperty(cx, id, tvr.addr()); |
|
|
|
|
older = JS_SetErrorReporter(cx, NULL); |
|
|
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); |
|
|
id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); |
|
|
ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value); |
|
577 |
JS_SetErrorReporter(cx, older); |
JS_SetErrorReporter(cx, older); |
578 |
if (ok) { |
if (!ok) |
579 |
*lengthp = ValueIsLength(cx, &tvr.u.value); |
return false; |
580 |
ok = !JSVAL_IS_NULL(tvr.u.value); |
|
581 |
} |
*lengthp = ValueIsLength(cx, tvr.addr()); |
582 |
JS_POP_TEMP_ROOT(cx, &tvr); |
return !JSVAL_IS_NULL(tvr.value()); |
|
return ok; |
|
583 |
} |
} |
584 |
|
|
585 |
JSBool |
JSBool |
587 |
{ |
{ |
588 |
JSClass *clasp; |
JSClass *clasp; |
589 |
|
|
590 |
clasp = OBJ_GET_CLASS(cx, obj); |
clasp = OBJ_GET_CLASS(cx, js_GetWrappedObject(cx, obj)); |
591 |
*answerp = (clasp == &js_ArgumentsClass || clasp == &js_ArrayClass || |
*answerp = (clasp == &js_ArgumentsClass || clasp == &js_ArrayClass || |
592 |
clasp == &js_SlowArrayClass); |
clasp == &js_SlowArrayClass); |
593 |
if (!*answerp) { |
if (!*answerp) { |
631 |
if (!OBJ_IS_ARRAY(cx, obj)) { |
if (!OBJ_IS_ARRAY(cx, obj)) { |
632 |
jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); |
jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); |
633 |
|
|
634 |
return OBJ_DEFINE_PROPERTY(cx, obj, lengthId, *vp, NULL, NULL, |
return obj->defineProperty(cx, lengthId, *vp, NULL, NULL, JSPROP_ENUMERATE); |
|
JSPROP_ENUMERATE, NULL); |
|
635 |
} |
} |
636 |
|
|
637 |
newlen = ValueIsLength(cx, vp); |
newlen = ValueIsLength(cx, vp); |
652 |
|
|
653 |
if (OBJ_IS_DENSE_ARRAY(cx, obj)) { |
if (OBJ_IS_DENSE_ARRAY(cx, obj)) { |
654 |
/* Don't reallocate if we're not actually shrinking our slots. */ |
/* Don't reallocate if we're not actually shrinking our slots. */ |
655 |
jsuint oldsize = js_DenseArrayCapacity(obj); |
jsuint capacity = js_DenseArrayCapacity(obj); |
656 |
if (oldsize >= newlen && !ResizeSlots(cx, obj, oldsize, newlen)) |
if (capacity > newlen && !ResizeSlots(cx, obj, capacity, newlen)) |
657 |
return JS_FALSE; |
return JS_FALSE; |
658 |
} else if (oldlen - newlen < (1 << 24)) { |
} else if (oldlen - newlen < (1 << 24)) { |
659 |
do { |
do { |
675 |
if (!iter) |
if (!iter) |
676 |
return JS_FALSE; |
return JS_FALSE; |
677 |
|
|
678 |
/* Protect iter against GC in OBJ_DELETE_PROPERTY. */ |
/* Protect iter against GC under JSObject::deleteProperty. */ |
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 (;;) { |
686 |
if (JSVAL_IS_VOID(id)) |
if (JSVAL_IS_VOID(id)) |
687 |
break; |
break; |
688 |
if (js_IdIsIndex(id, &index) && index - newlen < gap) { |
if (js_IdIsIndex(id, &index) && index - newlen < gap) { |
689 |
ok = OBJ_DELETE_PROPERTY(cx, obj, id, &junk); |
ok = obj->deleteProperty(cx, id, &junk); |
690 |
if (!ok) |
if (!ok) |
691 |
break; |
break; |
692 |
} |
} |
736 |
*propp = NULL; |
*propp = NULL; |
737 |
return JS_TRUE; |
return JS_TRUE; |
738 |
} |
} |
739 |
return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); |
return proto->lookupProperty(cx, id, objp, propp); |
740 |
} |
} |
741 |
|
|
742 |
static void |
static void |
770 |
return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp); |
return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp); |
771 |
|
|
772 |
if (id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom)) { |
if (id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom)) { |
773 |
*vp = STOBJ_GET_SLOT(obj, JSSLOT_PROTO); |
*vp = OBJECT_TO_JSVAL(obj->getProto()); |
774 |
return JS_TRUE; |
return JS_TRUE; |
775 |
} |
} |
776 |
|
|
800 |
if (!js_NativeGet(cx, obj, obj2, sprop, vp)) |
if (!js_NativeGet(cx, obj, obj2, sprop, vp)) |
801 |
return JS_FALSE; |
return JS_FALSE; |
802 |
} |
} |
803 |
OBJ_DROP_PROPERTY(cx, obj2, prop); |
obj2->dropProperty(cx, prop); |
804 |
} |
} |
805 |
return JS_TRUE; |
return JS_TRUE; |
806 |
} |
} |
882 |
* exists. If we hit the end of the prototype chain, it's safe to set the |
* exists. If we hit the end of the prototype chain, it's safe to set the |
883 |
* element on the original object. |
* element on the original object. |
884 |
*/ |
*/ |
885 |
while ((obj = JSVAL_TO_OBJECT(obj->fslots[JSSLOT_PROTO])) != NULL) { |
while ((obj = obj->getProto()) != NULL) { |
886 |
/* |
/* |
887 |
* If the prototype is a non-native object (possibly a dense array), or |
* 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, |
* a native object (possibly a slow array) that has indexed properties, |
890 |
*/ |
*/ |
891 |
if (!OBJ_IS_NATIVE(obj)) |
if (!OBJ_IS_NATIVE(obj)) |
892 |
return JS_TRUE; |
return JS_TRUE; |
893 |
if (SCOPE_HAS_INDEXED_PROPERTIES(OBJ_SCOPE(obj))) |
if (OBJ_SCOPE(obj)->hadIndexedProperties()) |
894 |
return JS_TRUE; |
return JS_TRUE; |
895 |
} |
} |
896 |
return JS_FALSE; |
return JS_FALSE; |
897 |
} |
} |
898 |
|
|
899 |
#ifdef JS_TRACER |
#ifdef JS_TRACER |
|
JSBool FASTCALL |
|
|
js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, jsval v) |
|
|
{ |
|
|
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); |
|
900 |
|
|
901 |
|
static inline JSBool FASTCALL |
902 |
|
dense_grow(JSContext* cx, JSObject* obj, jsint i, jsval v) |
903 |
|
{ |
904 |
/* |
/* |
905 |
* Let the interpreter worry about negative array indexes. |
* Let the interpreter worry about negative array indexes. |
906 |
*/ |
*/ |
907 |
JS_ASSERT((MAX_DSLOTS_LENGTH > JSVAL_INT_MAX) == (sizeof(jsval) != sizeof(uint32))); |
JS_ASSERT((MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32) == (sizeof(jsval) != sizeof(uint32))); |
908 |
if (MAX_DSLOTS_LENGTH > JSVAL_INT_MAX) { |
if (MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32) { |
909 |
/* |
/* |
910 |
* Have to check for negative values bleeding through on 64-bit machines only, |
* Have to check for negative values bleeding through on 64-bit machines only, |
911 |
* since we can't allocate large enough arrays for this on 32-bit machines. |
* since we can't allocate large enough arrays for this on 32-bit machines. |
934 |
obj->dslots[u] = v; |
obj->dslots[u] = v; |
935 |
return JS_TRUE; |
return JS_TRUE; |
936 |
} |
} |
937 |
|
|
938 |
|
|
939 |
|
JSBool FASTCALL |
940 |
|
js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, jsval v) |
941 |
|
{ |
942 |
|
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); |
943 |
|
return dense_grow(cx, obj, i, v); |
944 |
|
} |
945 |
|
JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem, CONTEXT, OBJECT, INT32, JSVAL, 0, 0) |
946 |
|
|
947 |
|
JSBool FASTCALL |
948 |
|
js_Array_dense_setelem_int(JSContext* cx, JSObject* obj, jsint i, int32 j) |
949 |
|
{ |
950 |
|
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); |
951 |
|
|
952 |
|
jsval v; |
953 |
|
if (JS_LIKELY(INT_FITS_IN_JSVAL(j))) { |
954 |
|
v = INT_TO_JSVAL(j); |
955 |
|
} else { |
956 |
|
jsdouble d = (jsdouble)j; |
957 |
|
if (!js_NewDoubleInRootedValue(cx, d, &v)) |
958 |
|
return JS_FALSE; |
959 |
|
} |
960 |
|
|
961 |
|
return dense_grow(cx, obj, i, v); |
962 |
|
} |
963 |
|
JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem_int, CONTEXT, OBJECT, INT32, INT32, 0, 0) |
964 |
|
|
965 |
|
JSBool FASTCALL |
966 |
|
js_Array_dense_setelem_double(JSContext* cx, JSObject* obj, jsint i, jsdouble d) |
967 |
|
{ |
968 |
|
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); |
969 |
|
|
970 |
|
jsval v; |
971 |
|
jsint j; |
972 |
|
|
973 |
|
if (JS_LIKELY(JSDOUBLE_IS_INT(d, j) && INT_FITS_IN_JSVAL(j))) { |
974 |
|
v = INT_TO_JSVAL(j); |
975 |
|
} else { |
976 |
|
if (!js_NewDoubleInRootedValue(cx, d, &v)) |
977 |
|
return JS_FALSE; |
978 |
|
} |
979 |
|
|
980 |
|
return dense_grow(cx, obj, i, v); |
981 |
|
} |
982 |
|
JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem_double, CONTEXT, OBJECT, INT32, DOUBLE, 0, 0) |
983 |
#endif |
#endif |
984 |
|
|
985 |
static JSBool |
static JSBool |
986 |
array_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, |
array_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, |
987 |
JSPropertyOp getter, JSPropertyOp setter, uintN attrs, |
JSPropertyOp getter, JSPropertyOp setter, uintN attrs) |
|
JSProperty **propp) |
|
988 |
{ |
{ |
989 |
uint32 i; |
uint32 i; |
990 |
JSBool isIndex; |
JSBool isIndex; |
993 |
return JS_TRUE; |
return JS_TRUE; |
994 |
|
|
995 |
isIndex = js_IdIsIndex(ID_TO_VALUE(id), &i); |
isIndex = js_IdIsIndex(ID_TO_VALUE(id), &i); |
996 |
if (!isIndex || attrs != JSPROP_ENUMERATE) { |
if (!isIndex || attrs != JSPROP_ENUMERATE || !OBJ_IS_DENSE_ARRAY(cx, obj) || INDEX_TOO_SPARSE(obj, i)) { |
997 |
if (!ENSURE_SLOW_ARRAY(cx, obj)) |
if (!ENSURE_SLOW_ARRAY(cx, obj)) |
998 |
return JS_FALSE; |
return JS_FALSE; |
999 |
return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, propp); |
return js_DefineProperty(cx, obj, id, value, getter, setter, attrs); |
1000 |
} |
} |
1001 |
|
|
1002 |
return array_setProperty(cx, obj, id, &value); |
return array_setProperty(cx, obj, id, &value); |
1065 |
* which is an int-tagged pointer that js_Enumerate creates, we set not one |
* which is an int-tagged pointer that js_Enumerate creates, we set not one |
1066 |
* but two lowest bits when tagging a JSIndexIterState pointer -- see |
* but two lowest bits when tagging a JSIndexIterState pointer -- see |
1067 |
* INDEX_ITER_TAG usage below. Thus, when slowarray_enumerate receives a state |
* INDEX_ITER_TAG usage below. Thus, when slowarray_enumerate receives a state |
1068 |
* tagged with JSVAL_BOOLEAN or with two lowest bits set, it knows that this |
* tagged with JSVAL_SPECIAL or with two lowest bits set, it knows that this |
1069 |
* is a fast state so it calls array_enumerate to continue enumerating the |
* is a fast state so it calls array_enumerate to continue enumerating the |
1070 |
* indexes present in the original fast array. |
* indexes present in the original fast array. |
1071 |
*/ |
*/ |
1073 |
#define PACKED_UINT_PAIR_BITS 14 |
#define PACKED_UINT_PAIR_BITS 14 |
1074 |
#define PACKED_UINT_PAIR_MASK JS_BITMASK(PACKED_UINT_PAIR_BITS) |
#define PACKED_UINT_PAIR_MASK JS_BITMASK(PACKED_UINT_PAIR_BITS) |
1075 |
|
|
1076 |
#define UINT_PAIR_TO_BOOLEAN_JSVAL(i,j) \ |
#define UINT_PAIR_TO_SPECIAL_JSVAL(i,j) \ |
1077 |
(JS_ASSERT((uint32) (i) <= PACKED_UINT_PAIR_MASK), \ |
(JS_ASSERT((uint32) (i) <= PACKED_UINT_PAIR_MASK), \ |
1078 |
JS_ASSERT((uint32) (j) <= PACKED_UINT_PAIR_MASK), \ |
JS_ASSERT((uint32) (j) <= PACKED_UINT_PAIR_MASK), \ |
1079 |
((jsval) (i) << (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)) | \ |
((jsval) (i) << (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)) | \ |
1080 |
((jsval) (j) << (JSVAL_TAGBITS)) | \ |
((jsval) (j) << (JSVAL_TAGBITS)) | \ |
1081 |
(jsval) JSVAL_BOOLEAN) |
(jsval) JSVAL_SPECIAL) |
1082 |
|
|
1083 |
#define BOOLEAN_JSVAL_TO_UINT_PAIR(v,i,j) \ |
#define SPECIAL_JSVAL_TO_UINT_PAIR(v,i,j) \ |
1084 |
(JS_ASSERT(JSVAL_TAG(v) == JSVAL_BOOLEAN), \ |
(JS_ASSERT(JSVAL_IS_SPECIAL(v)), \ |
1085 |
(i) = (uint32) ((v) >> (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)), \ |
(i) = (uint32) ((v) >> (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)), \ |
1086 |
(j) = (uint32) ((v) >> JSVAL_TAGBITS) & PACKED_UINT_PAIR_MASK, \ |
(j) = (uint32) ((v) >> JSVAL_TAGBITS) & PACKED_UINT_PAIR_MASK, \ |
1087 |
JS_ASSERT((i) <= PACKED_UINT_PAIR_MASK)) |
JS_ASSERT((i) <= PACKED_UINT_PAIR_MASK)) |
1122 |
if (obj->dslots[i] == JSVAL_HOLE) { |
if (obj->dslots[i] == JSVAL_HOLE) { |
1123 |
if (!ii) { |
if (!ii) { |
1124 |
ii = (JSIndexIterState *) |
ii = (JSIndexIterState *) |
1125 |
JS_malloc(cx, offsetof(JSIndexIterState, holes) + |
cx->malloc(offsetof(JSIndexIterState, holes) + |
1126 |
JS_BITMAP_SIZE(capacity)); |
JS_BITMAP_SIZE(capacity)); |
1127 |
if (!ii) |
if (!ii) |
1128 |
return JS_FALSE; |
return JS_FALSE; |
1135 |
if (!ii) { |
if (!ii) { |
1136 |
/* Array has no holes. */ |
/* Array has no holes. */ |
1137 |
if (capacity <= PACKED_UINT_PAIR_MASK) { |
if (capacity <= PACKED_UINT_PAIR_MASK) { |
1138 |
*statep = UINT_PAIR_TO_BOOLEAN_JSVAL(0, capacity); |
*statep = UINT_PAIR_TO_SPECIAL_JSVAL(0, capacity); |
1139 |
break; |
break; |
1140 |
} |
} |
1141 |
ii = (JSIndexIterState *) |
ii = (JSIndexIterState *) |
1142 |
JS_malloc(cx, offsetof(JSIndexIterState, holes)); |
cx->malloc(offsetof(JSIndexIterState, holes)); |
1143 |
if (!ii) |
if (!ii) |
1144 |
return JS_FALSE; |
return JS_FALSE; |
1145 |
ii->hasHoles = JS_FALSE; |
ii->hasHoles = JS_FALSE; |
1151 |
break; |
break; |
1152 |
|
|
1153 |
case JSENUMERATE_NEXT: |
case JSENUMERATE_NEXT: |
1154 |
if (JSVAL_TAG(*statep) == JSVAL_BOOLEAN) { |
if (JSVAL_IS_SPECIAL(*statep)) { |
1155 |
BOOLEAN_JSVAL_TO_UINT_PAIR(*statep, i, capacity); |
SPECIAL_JSVAL_TO_UINT_PAIR(*statep, i, capacity); |
1156 |
if (i != capacity) { |
if (i != capacity) { |
1157 |
*idp = INT_TO_JSID(i); |
*idp = INT_TO_JSID(i); |
1158 |
*statep = UINT_PAIR_TO_BOOLEAN_JSVAL(i + 1, capacity); |
*statep = UINT_PAIR_TO_SPECIAL_JSVAL(i + 1, capacity); |
1159 |
break; |
break; |
1160 |
} |
} |
1161 |
} else { |
} else { |
1177 |
/* FALL THROUGH */ |
/* FALL THROUGH */ |
1178 |
|
|
1179 |
case JSENUMERATE_DESTROY: |
case JSENUMERATE_DESTROY: |
1180 |
if (JSVAL_TAG(*statep) != JSVAL_BOOLEAN) { |
if (!JSVAL_IS_SPECIAL(*statep)) { |
1181 |
JS_ASSERT((*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG); |
JS_ASSERT((*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG); |
1182 |
ii = (JSIndexIterState *) (*statep & ~INDEX_ITER_TAG); |
ii = (JSIndexIterState *) (*statep & ~INDEX_ITER_TAG); |
1183 |
JS_free(cx, ii); |
cx->free(ii); |
1184 |
} |
} |
1185 |
*statep = JSVAL_NULL; |
*statep = JSVAL_NULL; |
1186 |
break; |
break; |
1196 |
|
|
1197 |
/* Are we continuing an enumeration that started when we were dense? */ |
/* Are we continuing an enumeration that started when we were dense? */ |
1198 |
if (enum_op != JSENUMERATE_INIT) { |
if (enum_op != JSENUMERATE_INIT) { |
1199 |
if (JSVAL_TAG(*statep) == JSVAL_BOOLEAN || |
if (JSVAL_IS_SPECIAL(*statep) || |
1200 |
(*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG) { |
(*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG) { |
1201 |
return array_enumerate(cx, obj, enum_op, statep, idp); |
return array_enumerate(cx, obj, enum_op, statep, idp); |
1202 |
} |
} |
1211 |
array_finalize(JSContext *cx, JSObject *obj) |
array_finalize(JSContext *cx, JSObject *obj) |
1212 |
{ |
{ |
1213 |
if (obj->dslots) |
if (obj->dslots) |
1214 |
JS_free(cx, obj->dslots - 1); |
cx->free(obj->dslots - 1); |
1215 |
obj->dslots = NULL; |
obj->dslots = NULL; |
1216 |
} |
} |
1217 |
|
|
1222 |
size_t i; |
size_t i; |
1223 |
jsval v; |
jsval v; |
1224 |
|
|
1225 |
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj)); |
JS_ASSERT(js_IsDenseArray(obj)); |
1226 |
|
obj->traceProtoAndParent(trc); |
1227 |
|
|
1228 |
capacity = js_DenseArrayCapacity(obj); |
capacity = js_DenseArrayCapacity(obj); |
1229 |
for (i = 0; i < capacity; i++) { |
for (i = 0; i < capacity; i++) { |
1233 |
JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v)); |
JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v)); |
1234 |
} |
} |
1235 |
} |
} |
|
|
|
|
for (i = JSSLOT_PROTO; i <= JSSLOT_PARENT; ++i) { |
|
|
v = STOBJ_GET_SLOT(obj, i); |
|
|
if (JSVAL_IS_TRACEABLE(v)) { |
|
|
JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i); |
|
|
JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v)); |
|
|
} |
|
|
} |
|
1236 |
} |
} |
1237 |
|
|
1238 |
extern JSObjectOps js_ArrayObjectOps; |
extern JSObjectOps js_ArrayObjectOps; |
1239 |
|
|
1240 |
static const JSObjectMap SharedArrayMap = { &js_ArrayObjectOps }; |
static const JSObjectMap SharedArrayMap(&js_ArrayObjectOps, JSObjectMap::SHAPELESS); |
1241 |
|
|
1242 |
JSObjectOps js_ArrayObjectOps = { |
JSObjectOps js_ArrayObjectOps = { |
1243 |
&SharedArrayMap, |
&SharedArrayMap, |
1249 |
NULL, array_dropProperty, |
NULL, array_dropProperty, |
1250 |
NULL, NULL, |
NULL, NULL, |
1251 |
js_HasInstance, array_trace, |
js_HasInstance, array_trace, |
|
NULL, NULL, |
|
1252 |
NULL |
NULL |
1253 |
}; |
}; |
1254 |
|
|
1260 |
|
|
1261 |
JSClass js_ArrayClass = { |
JSClass js_ArrayClass = { |
1262 |
"Array", |
"Array", |
1263 |
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | |
JSCLASS_HAS_RESERVED_SLOTS(2) | |
1264 |
JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_ENUMERATE, |
JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | |
1265 |
|
JSCLASS_NEW_ENUMERATE, |
1266 |
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, |
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, |
1267 |
JS_EnumerateStub, JS_ResolveStub, js_TryValueOf, array_finalize, |
JS_EnumerateStub, JS_ResolveStub, js_TryValueOf, array_finalize, |
1268 |
array_getObjectOps, NULL, NULL, NULL, |
array_getObjectOps, NULL, NULL, NULL, |
1271 |
|
|
1272 |
JSClass js_SlowArrayClass = { |
JSClass js_SlowArrayClass = { |
1273 |
"Array", |
"Array", |
1274 |
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array), |
JSCLASS_HAS_RESERVED_SLOTS(1) | |
1275 |
|
JSCLASS_HAS_CACHED_PROTO(JSProto_Array), |
1276 |
slowarray_addProperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, |
slowarray_addProperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, |
1277 |
JS_EnumerateStub, JS_ResolveStub, js_TryValueOf, JS_FinalizeStub, |
JS_EnumerateStub, JS_ResolveStub, js_TryValueOf, NULL, |
1278 |
slowarray_getObjectOps, NULL, NULL, NULL, |
slowarray_getObjectOps, NULL, NULL, NULL, |
1279 |
NULL, NULL, NULL, NULL |
NULL, NULL, NULL, NULL |
1280 |
}; |
}; |
1285 |
JSBool |
JSBool |
1286 |
js_MakeArraySlow(JSContext *cx, JSObject *obj) |
js_MakeArraySlow(JSContext *cx, JSObject *obj) |
1287 |
{ |
{ |
1288 |
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass); |
JS_ASSERT(obj->getClass() == &js_ArrayClass); |
1289 |
|
|
1290 |
/* Create a native scope. */ |
/* |
1291 |
JSScope *scope = js_NewScope(cx, &js_SlowArrayObjectOps, |
* Create a native scope. All slow arrays other than Array.prototype get |
1292 |
&js_SlowArrayClass, obj); |
* the same initial shape. |
1293 |
|
*/ |
1294 |
|
uint32 emptyShape; |
1295 |
|
JSObject *arrayProto = obj->getProto(); |
1296 |
|
if (arrayProto->getClass() == &js_ObjectClass) { |
1297 |
|
/* obj is Array.prototype. */ |
1298 |
|
emptyShape = js_GenerateShape(cx, false); |
1299 |
|
} else { |
1300 |
|
/* arrayProto is Array.prototype. */ |
1301 |
|
JS_ASSERT(arrayProto->getClass() == &js_SlowArrayClass); |
1302 |
|
emptyShape = OBJ_SCOPE(arrayProto)->emptyScope->shape; |
1303 |
|
} |
1304 |
|
JSScope *scope = JSScope::create(cx, &js_SlowArrayObjectOps, &js_SlowArrayClass, obj, |
1305 |
|
emptyShape); |
1306 |
if (!scope) |
if (!scope) |
1307 |
return JS_FALSE; |
return JS_FALSE; |
1308 |
|
|
1327 |
continue; |
continue; |
1328 |
} |
} |
1329 |
|
|
1330 |
sprop = js_AddScopeProperty(cx, scope, id, NULL, NULL, |
sprop = scope->add(cx, id, NULL, NULL, i + JS_INITIAL_NSLOTS, |
1331 |
i + JS_INITIAL_NSLOTS, JSPROP_ENUMERATE, |
JSPROP_ENUMERATE, 0, 0); |
|
0, 0); |
|
1332 |
if (!sprop) |
if (!sprop) |
1333 |
goto out_bad; |
goto out_bad; |
1334 |
} |
} |
1350 |
obj->classword ^= (jsuword) &js_ArrayClass; |
obj->classword ^= (jsuword) &js_ArrayClass; |
1351 |
obj->classword |= (jsuword) &js_SlowArrayClass; |
obj->classword |= (jsuword) &js_SlowArrayClass; |
1352 |
|
|
1353 |
obj->map = &scope->map; |
obj->map = scope; |
1354 |
return JS_TRUE; |
return JS_TRUE; |
1355 |
|
|
1356 |
out_bad: |
out_bad: |
1357 |
js_DestroyScope(cx, scope); |
JSScope::destroy(cx, scope); |
1358 |
return JS_FALSE; |
return JS_FALSE; |
1359 |
} |
} |
1360 |
|
|
1361 |
enum ArrayToStringOp { |
/* Transfer ownership of buffer to returned string. */ |
1362 |
TO_STRING, |
static inline JSBool |
1363 |
TO_LOCALE_STRING, |
BufferToString(JSContext *cx, JSCharBuffer &cb, jsval *rval) |
1364 |
TO_SOURCE |
{ |
1365 |
}; |
JSString *str = js_NewStringFromCharBuffer(cx, cb); |
1366 |
|
if (!str) |
1367 |
|
return false; |
1368 |
|
*rval = STRING_TO_JSVAL(str); |
1369 |
|
return true; |
1370 |
|
} |
1371 |
|
|
1372 |
/* |
#if JS_HAS_TOSOURCE |
|
* When op is TO_STRING or TO_LOCALE_STRING sep indicates a separator to use |
|
|
* or "," when sep is NULL. |
|
|
* When op is TO_SOURCE sep must be NULL. |
|
|
*/ |
|
1373 |
static JSBool |
static JSBool |
1374 |
array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op, |
array_toSource(JSContext *cx, uintN argc, jsval *vp) |
|
JSString *sep, jsval *rval) |
|
1375 |
{ |
{ |
1376 |
JSBool ok, hole; |
JS_CHECK_RECURSION(cx, return false); |
|
jsuint length, index; |
|
|
jschar *chars, *ochars; |
|
|
size_t nchars, growth, seplen, tmplen, extratail; |
|
|
const jschar *sepstr; |
|
|
JSString *str; |
|
|
JSHashEntry *he; |
|
|
JSAtom *atom; |
|
|
|
|
|
JS_CHECK_RECURSION(cx, return JS_FALSE); |
|
1377 |
|
|
1378 |
ok = js_GetLengthProperty(cx, obj, &length); |
JSObject *obj = JS_THIS_OBJECT(cx, vp); |
1379 |
if (!ok) |
if (!obj || |
1380 |
return JS_FALSE; |
(OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass && |
1381 |
|
!JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) { |
1382 |
|
return false; |
1383 |
|
} |
1384 |
|
|
1385 |
he = js_EnterSharpObject(cx, obj, NULL, &chars); |
/* Find joins or cycles in the reachable object graph. */ |
1386 |
|
jschar *sharpchars; |
1387 |
|
JSHashEntry *he = js_EnterSharpObject(cx, obj, NULL, &sharpchars); |
1388 |
if (!he) |
if (!he) |
1389 |
return JS_FALSE; |
return false; |
1390 |
#ifdef DEBUG |
bool initiallySharp = IS_SHARP(he); |
1391 |
growth = (size_t) -1; |
|
1392 |
#endif |
/* After this point, all paths exit through the 'out' label. */ |
1393 |
|
MUST_FLOW_THROUGH("out"); |
1394 |
|
bool ok = false; |
1395 |
|
|
1396 |
/* |
/* |
1397 |
* We must check for the sharp bit and skip js_LeaveSharpObject when it is |
* This object will take responsibility for the jschar buffer until the |
1398 |
* set even when op is not TO_SOURCE. A script can overwrite the default |
* buffer is transferred to the returned JSString. |
|
* toSource implementation and trigger a call, for example, to the |
|
|
* toString method during serialization of the object graph (bug 369696). |
|
1399 |
*/ |
*/ |
1400 |
if (IS_SHARP(he)) { |
JSCharBuffer cb(cx); |
1401 |
|
|
1402 |
|
/* Cycles/joins are indicated by sharp objects. */ |
1403 |
#if JS_HAS_SHARP_VARS |
#if JS_HAS_SHARP_VARS |
1404 |
nchars = js_strlen(chars); |
if (IS_SHARP(he)) { |
1405 |
|
JS_ASSERT(sharpchars != 0); |
1406 |
|
cb.replaceRawBuffer(sharpchars, js_strlen(sharpchars)); |
1407 |
|
goto make_string; |
1408 |
|
} else if (sharpchars) { |
1409 |
|
MAKE_SHARP(he); |
1410 |
|
cb.replaceRawBuffer(sharpchars, js_strlen(sharpchars)); |
1411 |
|
} |
1412 |
#else |
#else |
1413 |
chars[0] = '['; |
if (IS_SHARP(he)) { |
1414 |
chars[1] = ']'; |
if (!js_AppendLiteral(cb, "[]")) |
1415 |
chars[2] = 0; |
goto out; |
1416 |
nchars = 2; |
cx->free(sharpchars); |
|
#endif |
|
1417 |
goto make_string; |
goto make_string; |
1418 |
} |
} |
1419 |
|
#endif |
1420 |
|
|
1421 |
if (op == TO_SOURCE) { |
if (!cb.append('[')) |
1422 |
/* |
goto out; |
|
* Always allocate 2 extra chars for closing ']' and terminating 0 |
|
|
* and then preallocate 1 + extratail to include starting '['. |
|
|
*/ |
|
|
extratail = 2; |
|
|
growth = (1 + extratail) * sizeof(jschar); |
|
|
if (!chars) { |
|
|
nchars = 0; |
|
|
chars = (jschar *) malloc(growth); |
|
|
if (!chars) |
|
|
goto done; |
|
|
} else { |
|
|
MAKE_SHARP(he); |
|
|
nchars = js_strlen(chars); |
|
|
growth += nchars * sizeof(jschar); |
|
|
chars = (jschar *)realloc((ochars = chars), growth); |
|
|
if (!chars) { |
|
|
free(ochars); |
|
|
goto done; |
|
|
} |
|
|
} |
|
|
chars[nchars++] = '['; |
|
|
JS_ASSERT(sep == NULL); |
|
|
sepstr = NULL; /* indicates to use ", " as separator */ |
|
|
seplen = 2; |
|
|
} else { |
|
|
/* |
|
|
* Free any sharp variable definition in chars. Normally, we would |
|
|
* MAKE_SHARP(he) so that only the first sharp variable annotation is |
|
|
* a definition, and all the rest are references, but in the current |
|
|
* case of (op != TO_SOURCE), we don't need chars at all. |
|
|
*/ |
|
|
if (chars) |
|
|
JS_free(cx, chars); |
|
|
chars = NULL; |
|
|
nchars = 0; |
|
|
extratail = 1; /* allocate extra char for terminating 0 */ |
|
|
|
|
|
/* Return the empty string on a cycle as well as on empty join. */ |
|
|
if (IS_BUSY(he) || length == 0) { |
|
|
js_LeaveSharpObject(cx, NULL); |
|
|
*rval = JS_GetEmptyStringValue(cx); |
|
|
return ok; |
|
|
} |
|
1423 |
|
|
1424 |
/* Flag he as BUSY so we can distinguish a cycle from a join-point. */ |
jsuint length; |
1425 |
MAKE_BUSY(he); |
if (!js_GetLengthProperty(cx, obj, &length)) |
1426 |
|
goto out; |
1427 |
|
|
1428 |
if (sep) { |
for (jsuint index = 0; index < length; index++) { |
1429 |
JSSTRING_CHARS_AND_LENGTH(sep, sepstr, seplen); |
/* Use vp to locally root each element value. */ |
1430 |
} else { |
JSBool hole; |
1431 |
sepstr = NULL; /* indicates to use "," as separator */ |
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
1432 |
seplen = 1; |
!GetArrayElement(cx, obj, index, &hole, vp)) { |
1433 |
|
goto out; |
1434 |
} |
} |
|
} |
|
1435 |
|
|
1436 |
/* Use rval to locally root each element value as we loop and convert. */ |
/* Get element's character string. */ |
1437 |
for (index = 0; index < length; index++) { |
JSString *str; |
1438 |
ok = (JS_CHECK_OPERATION_LIMIT(cx) && |
if (hole) { |
|
GetArrayElement(cx, obj, index, &hole, rval)); |
|
|
if (!ok) |
|
|
goto done; |
|
|
if (hole || |
|
|
(op != TO_SOURCE && |
|
|
(JSVAL_IS_VOID(*rval) || JSVAL_IS_NULL(*rval)))) { |
|
1439 |
str = cx->runtime->emptyString; |
str = cx->runtime->emptyString; |
1440 |
} else { |
} else { |
1441 |
if (op == TO_LOCALE_STRING) { |
str = js_ValueToSource(cx, *vp); |
1442 |
JSObject *robj; |
if (!str) |
1443 |
|
goto out; |
1444 |
|
} |
1445 |
|
*vp = STRING_TO_JSVAL(str); |
1446 |
|
const jschar *chars; |
1447 |
|
size_t charlen; |
1448 |
|
str->getCharsAndLength(chars, charlen); |
1449 |
|
|
1450 |
atom = cx->runtime->atomState.toLocaleStringAtom; |
/* Append element to buffer. */ |
1451 |
ok = js_ValueToObject(cx, *rval, &robj); |
if (!cb.append(chars, charlen)) |
1452 |
if (ok) { |
goto out; |
1453 |
/* Re-use *rval to protect robj temporarily. */ |
if (index + 1 != length) { |
1454 |
*rval = OBJECT_TO_JSVAL(robj); |
if (!js_AppendLiteral(cb, ", ")) |
1455 |
ok = js_TryMethod(cx, robj, atom, 0, NULL, rval); |
goto out; |
1456 |
} |
} else if (hole) { |
1457 |
if (!ok) |
if (!cb.append(',')) |
1458 |
goto done; |
goto out; |
|
str = js_ValueToString(cx, *rval); |
|
|
} else if (op == TO_STRING) { |
|
|
str = js_ValueToString(cx, *rval); |
|
|
} else { |
|
|
JS_ASSERT(op == TO_SOURCE); |
|
|
str = js_ValueToSource(cx, *rval); |
|
|
} |
|
|
if (!str) { |
|
|
ok = JS_FALSE; |
|
|
goto done; |
|
|
} |
|
1459 |
} |
} |
1460 |
|
} |
1461 |
|
|
1462 |
/* |
/* Finalize the buffer. */ |
1463 |
* Do not append separator after the last element unless it is a hole |
if (!cb.append(']')) |
1464 |
* and we are in toSource. In that case we append single ",". |
goto out; |
|
*/ |
|
|
if (index + 1 == length) |
|
|
seplen = (hole && op == TO_SOURCE) ? 1 : 0; |
|
1465 |
|
|
1466 |
/* Allocate 1 at end for closing bracket and zero. */ |
make_string: |
1467 |
tmplen = JSSTRING_LENGTH(str); |
if (!BufferToString(cx, cb, vp)) |
1468 |
growth = nchars + tmplen + seplen + extratail; |
goto out; |
|
if (nchars > growth || tmplen > growth || |
|
|
growth > (size_t)-1 / sizeof(jschar)) { |
|
|
if (chars) { |
|
|
free(chars); |
|
|
chars = NULL; |
|
|
} |
|
|
goto done; |
|
|
} |
|
|
growth *= sizeof(jschar); |
|
|
if (!chars) { |
|
|
chars = (jschar *) malloc(growth); |
|
|
if (!chars) |
|
|
goto done; |
|
|
} else { |
|
|
chars = (jschar *) realloc((ochars = chars), growth); |
|
|
if (!chars) { |
|
|
free(ochars); |
|
|
goto done; |
|
|
} |
|
|
} |
|
1469 |
|
|
1470 |
js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen); |
ok = true; |
|
nchars += tmplen; |
|
1471 |
|
|
1472 |
if (seplen) { |
out: |
1473 |
if (sepstr) { |
if (!initiallySharp) |
1474 |
js_strncpy(&chars[nchars], sepstr, seplen); |
js_LeaveSharpObject(cx, NULL); |
1475 |
} else { |
return ok; |
1476 |
JS_ASSERT(seplen == 1 || seplen == 2); |
} |
1477 |
chars[nchars] = ','; |
#endif |
1478 |
if (seplen == 2) |
|
1479 |
chars[nchars + 1] = ' '; |
static JSHashNumber |
1480 |
} |
js_hash_array(const void *key) |
1481 |
nchars += seplen; |
{ |
1482 |
|
return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; |
1483 |
|
} |
1484 |
|
|
1485 |
|
bool |
1486 |
|
js_InitContextBusyArrayTable(JSContext *cx) |
1487 |
|
{ |
1488 |
|
cx->busyArrayTable = JS_NewHashTable(4, js_hash_array, JS_CompareValues, |
1489 |
|
JS_CompareValues, NULL, NULL); |
1490 |
|
return cx->busyArrayTable != NULL; |
1491 |
|
} |
1492 |
|
|
1493 |
|
static JSBool |
1494 |
|
array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, |
1495 |
|
JSString *sepstr, jsval *rval) |
1496 |
|
{ |
1497 |
|
JS_CHECK_RECURSION(cx, return false); |
1498 |
|
|
1499 |
|
/* |
1500 |
|
* This hash table is shared between toString invocations and must be empty |
1501 |
|
* after the root invocation completes. |
1502 |
|
*/ |
1503 |
|
JSHashTable *table = cx->busyArrayTable; |
1504 |
|
|
1505 |
|
/* |
1506 |
|
* Use HashTable entry as the cycle indicator. On first visit, create the |
1507 |
|
* entry, and, when leaving, remove the entry. |
1508 |
|
*/ |
1509 |
|
JSHashNumber hash = js_hash_array(obj); |
1510 |
|
JSHashEntry **hep = JS_HashTableRawLookup(table, hash, obj); |
1511 |
|
JSHashEntry *he = *hep; |
1512 |
|
if (!he) { |
1513 |
|
/* Not in hash table, so not a cycle. */ |
1514 |
|
he = JS_HashTableRawAdd(table, hep, hash, obj, NULL); |
1515 |
|
if (!he) { |
1516 |
|
JS_ReportOutOfMemory(cx); |
1517 |
|
return false; |
1518 |
} |
} |
1519 |
|
} else { |
1520 |
|
/* Cycle, so return empty string. */ |
1521 |
|
*rval = ATOM_KEY(cx->runtime->atomState.emptyAtom); |
1522 |
|
return true; |
1523 |
} |
} |
1524 |
|
|
1525 |
done: |
JSAutoTempValueRooter tvr(cx, obj); |
1526 |
if (op == TO_SOURCE) { |
|
1527 |
if (chars) |
/* After this point, all paths exit through the 'out' label. */ |
1528 |
chars[nchars++] = ']'; |
MUST_FLOW_THROUGH("out"); |
1529 |
|
bool ok = false; |
1530 |
|
|
1531 |
|
/* Get characters to use for the separator. */ |
1532 |
|
static const jschar comma = ','; |
1533 |
|
const jschar *sep; |
1534 |
|
size_t seplen; |
1535 |
|
if (sepstr) { |
1536 |
|
sepstr->getCharsAndLength(sep, seplen); |
1537 |
} else { |
} else { |
1538 |
CLEAR_BUSY(he); |
sep = , |
1539 |
} |
seplen = 1; |
|
js_LeaveSharpObject(cx, NULL); |
|
|
if (!ok) { |
|
|
if (chars) |
|
|
free(chars); |
|
|
return ok; |
|
1540 |
} |
} |
1541 |
|
|
1542 |
make_string: |
/* |
1543 |
if (!chars) { |
* This object will take responsibility for the jschar buffer until the |
1544 |
JS_ReportOutOfMemory(cx); |
* buffer is transferred to the returned JSString. |
1545 |
return JS_FALSE; |
*/ |
1546 |
} |
JSCharBuffer cb(cx); |
|
chars[nchars] = 0; |
|
|
JS_ASSERT(growth == (size_t)-1 || (nchars + 1) * sizeof(jschar) == growth); |
|
|
str = js_NewString(cx, chars, nchars); |
|
|
if (!str) { |
|
|
free(chars); |
|
|
return JS_FALSE; |
|
|
} |
|
|
*rval = STRING_TO_JSVAL(str); |
|
|
return JS_TRUE; |
|
|
} |
|
1547 |
|
|
1548 |
#if JS_HAS_TOSOURCE |
jsuint length; |
1549 |
static JSBool |
if (!js_GetLengthProperty(cx, obj, &length)) |
1550 |
array_toSource(JSContext *cx, uintN argc, jsval *vp) |
goto out; |
|
{ |
|
|
JSObject *obj; |
|
1551 |
|
|
1552 |
obj = JS_THIS_OBJECT(cx, vp); |
for (jsuint index = 0; index < length; index++) { |
1553 |
if (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass && |
/* Use rval to locally root each element value. */ |
1554 |
!JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2)) { |
JSBool hole; |
1555 |
return JS_FALSE; |
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
1556 |
|
!GetArrayElement(cx, obj, index, &hole, rval)) { |
1557 |
|
goto out; |
1558 |
|
} |
1559 |
|
|
1560 |
|
/* Get element's character string. */ |
1561 |
|
if (!(hole || JSVAL_IS_VOID(*rval) || JSVAL_IS_NULL(*rval))) { |
1562 |
|
if (locale) { |
1563 |
|
/* Work on obj.toLocalString() instead. */ |
1564 |
|
JSObject *robj; |
1565 |
|
|
1566 |
|
if (!js_ValueToObject(cx, *rval, &robj)) |
1567 |
|
goto out; |
1568 |
|
*rval = OBJECT_TO_JSVAL(robj); |
1569 |
|
JSAtom *atom = cx->runtime->atomState.toLocaleStringAtom; |
1570 |
|
if (!js_TryMethod(cx, robj, atom, 0, NULL, rval)) |
1571 |
|
goto out; |
1572 |
|
} |
1573 |
|
|
1574 |
|
if (!js_ValueToCharBuffer(cx, *rval, cb)) |
1575 |
|
goto out; |
1576 |
|
} |
1577 |
|
|
1578 |
|
/* Append the separator. */ |
1579 |
|
if (index + 1 != length) { |
1580 |
|
if (!cb.append(sep, seplen)) |
1581 |
|
goto out; |
1582 |
|
} |
1583 |
} |
} |
1584 |
return array_join_sub(cx, obj, TO_SOURCE, NULL, vp); |
|
1585 |
|
/* Finalize the buffer. */ |
1586 |
|
if (!BufferToString(cx, cb, rval)) |
1587 |
|
goto out; |
1588 |
|
|
1589 |
|
ok = true; |
1590 |
|
|
1591 |
|
out: |
1592 |
|
/* |
1593 |
|
* It is possible that 'hep' may have been invalidated by subsequent |
1594 |
|
* RawAdd/Remove. Hence, 'RawRemove' must not be used. |
1595 |
|
*/ |
1596 |
|
JS_HashTableRemove(table, obj); |
1597 |
|
return ok; |
1598 |
} |
} |
|
#endif |
|
1599 |
|
|
1600 |
static JSBool |
static JSBool |
1601 |
array_toString(JSContext *cx, uintN argc, jsval *vp) |
array_toString(JSContext *cx, uintN argc, jsval *vp) |
1603 |
JSObject *obj; |
JSObject *obj; |
1604 |
|
|
1605 |
obj = JS_THIS_OBJECT(cx, vp); |
obj = JS_THIS_OBJECT(cx, vp); |
1606 |
if (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass && |
if (!obj || |
1607 |
!JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2)) { |
(OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass && |
1608 |
|
!JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) { |
1609 |
return JS_FALSE; |
return JS_FALSE; |
1610 |
} |
} |
1611 |
return array_join_sub(cx, obj, TO_STRING, NULL, vp); |
|
1612 |
|
return array_toString_sub(cx, obj, JS_FALSE, NULL, vp); |
1613 |
} |
} |
1614 |
|
|
1615 |
static JSBool |
static JSBool |
1618 |
JSObject *obj; |
JSObject *obj; |
1619 |
|
|
1620 |
obj = JS_THIS_OBJECT(cx, vp); |
obj = JS_THIS_OBJECT(cx, vp); |
1621 |
if (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass && |
if (!obj || |
1622 |
!JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2)) { |
(OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass && |
1623 |
|
!JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) { |
1624 |
return JS_FALSE; |
return JS_FALSE; |
1625 |
} |
} |
1626 |
|
|
1628 |
* Passing comma here as the separator. Need a way to get a |
* Passing comma here as the separator. Need a way to get a |
1629 |
* locale-specific version. |
* locale-specific version. |
1630 |
*/ |
*/ |
1631 |
return array_join_sub(cx, obj, TO_LOCALE_STRING, NULL, vp); |
return array_toString_sub(cx, obj, JS_TRUE, NULL, vp); |
1632 |
} |
} |
1633 |
|
|
1634 |
enum TargetElementsType { |
enum TargetElementsType { |
1666 |
JS_ASSERT(ReallyBigIndexToId(cx, index, idr.addr())); |
JS_ASSERT(ReallyBigIndexToId(cx, index, idr.addr())); |
1667 |
JSObject* obj2; |
JSObject* obj2; |
1668 |
JSProperty* prop; |
JSProperty* prop; |
1669 |
JS_ASSERT(OBJ_LOOKUP_PROPERTY(cx, obj, idr.id(), &obj2, &prop)); |
JS_ASSERT(obj->lookupProperty(cx, idr.id(), &obj2, &prop)); |
1670 |
JS_ASSERT(!prop); |
JS_ASSERT(!prop); |
1671 |
} |
} |
1672 |
} |
} |
1727 |
if (!dp) |
if (!dp) |
1728 |
return JS_FALSE; |
return JS_FALSE; |
1729 |
tmp[0] = DOUBLE_TO_JSVAL(dp); |
tmp[0] = DOUBLE_TO_JSVAL(dp); |
1730 |
JSAutoTempValueRooter(cx, JS_ARRAY_LENGTH(tmp), tmp); |
JSAutoTempValueRooter tvr(cx, JS_ARRAY_LENGTH(tmp), tmp); |
1731 |
JSAutoTempIdRooter idr(cx); |
JSAutoTempIdRooter idr(cx); |
1732 |
do { |
do { |
1733 |
tmp[1] = *vector++; |
tmp[1] = *vector++; |
1734 |
if (!js_ValueToStringId(cx, tmp[0], idr.addr()) || |
if (!js_ValueToStringId(cx, tmp[0], idr.addr()) || |
1735 |
!OBJ_SET_PROPERTY(cx, obj, idr.id(), &tmp[1])) { |
!obj->setProperty(cx, idr.id(), &tmp[1])) { |
1736 |
return JS_FALSE; |
return JS_FALSE; |
1737 |
} |
} |
1738 |
*dp += 1; |
*dp += 1; |
1775 |
Array_p_join(JSContext* cx, JSObject* obj, JSString *str) |
Array_p_join(JSContext* cx, JSObject* obj, JSString *str) |
1776 |
{ |
{ |
1777 |
JSAutoTempValueRooter tvr(cx); |
JSAutoTempValueRooter tvr(cx); |
1778 |
if (!array_join_sub(cx, obj, TO_STRING, str, tvr.addr())) { |
if (!array_toString_sub(cx, obj, JS_FALSE, str, tvr.addr())) { |
1779 |
js_SetBuiltinError(cx); |
js_SetBuiltinError(cx); |
1780 |
return NULL; |
return NULL; |
1781 |
} |
} |
1786 |
Array_p_toString(JSContext* cx, JSObject* obj) |
Array_p_toString(JSContext* cx, JSObject* obj) |
1787 |
{ |
{ |
1788 |
JSAutoTempValueRooter tvr(cx); |
JSAutoTempValueRooter tvr(cx); |
1789 |
if (!array_join_sub(cx, obj, TO_STRING, NULL, tvr.addr())) { |
if (!array_toString_sub(cx, obj, JS_FALSE, NULL, tvr.addr())) { |
1790 |
js_SetBuiltinError(cx); |
js_SetBuiltinError(cx); |
1791 |
return NULL; |
return NULL; |
1792 |
} |
} |
1812 |
vp[2] = STRING_TO_JSVAL(str); |
vp[2] = STRING_TO_JSVAL(str); |
1813 |
} |
} |
1814 |
obj = JS_THIS_OBJECT(cx, vp); |
obj = JS_THIS_OBJECT(cx, vp); |
1815 |
return obj && array_join_sub(cx, obj, TO_STRING, str, vp); |
return obj && array_toString_sub(cx, obj, JS_FALSE, str, vp); |
1816 |
} |
} |
1817 |
|
|
1818 |
static JSBool |
static JSBool |
1819 |
array_reverse(JSContext *cx, uintN argc, jsval *vp) |
array_reverse(JSContext *cx, uintN argc, jsval *vp) |
1820 |
{ |
{ |
1821 |
JSObject *obj; |
jsuint len; |
1822 |
JSTempValueRooter tvr; |
JSObject *obj = JS_THIS_OBJECT(cx, vp); |
|
jsuint len, half, i; |
|
|
JSBool ok, hole, hole2; |
|
|
|
|
|
obj = JS_THIS_OBJECT(cx, vp); |
|
1823 |
if (!obj || !js_GetLengthProperty(cx, obj, &len)) |
if (!obj || !js_GetLengthProperty(cx, obj, &len)) |
1824 |
return JS_FALSE; |
return JS_FALSE; |
1825 |
*vp = OBJECT_TO_JSVAL(obj); |
*vp = OBJECT_TO_JSVAL(obj); |
1857 |
return JS_TRUE; |
return JS_TRUE; |
1858 |
} |
} |
1859 |
|
|
1860 |
ok = JS_TRUE; |
JSAutoTempValueRooter tvr(cx, JSVAL_NULL); |
1861 |
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); |
for (jsuint i = 0, half = len / 2; i < half; i++) { |
1862 |
half = len / 2; |
JSBool hole, hole2; |
1863 |
for (i = 0; i < half; i++) { |
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
1864 |
ok = JS_CHECK_OPERATION_LIMIT(cx) && |
!GetArrayElement(cx, obj, i, &hole, tvr.addr()) || |
1865 |
GetArrayElement(cx, obj, i, &hole, &tvr.u.value) && |
!GetArrayElement(cx, obj, len - i - 1, &hole2, vp) || |
1866 |
GetArrayElement(cx, obj, len - i - 1, &hole2, vp) && |
!SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.value()) || |
1867 |
SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.u.value) && |
!SetOrDeleteArrayElement(cx, obj, i, hole2, *vp)) { |
1868 |
SetOrDeleteArrayElement(cx, obj, i, hole2, *vp); |
return false; |
1869 |
if (!ok) |
} |
|
break; |
|
1870 |
} |
} |
|
JS_POP_TEMP_ROOT(cx, &tvr); |
|
|
|
|
1871 |
*vp = OBJECT_TO_JSVAL(obj); |
*vp = OBJECT_TO_JSVAL(obj); |
1872 |
return ok; |
return true; |
1873 |
} |
} |
1874 |
|
|
1875 |
typedef struct MSortArgs { |
typedef struct MSortArgs { |
1880 |
} MSortArgs; |
} MSortArgs; |
1881 |
|
|
1882 |
/* Helper function for js_MergeSort. */ |
/* Helper function for js_MergeSort. */ |
1883 |
static JS_REQUIRES_STACK JSBool |
static JSBool |
1884 |
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) |
1885 |
{ |
{ |
1886 |
void *arg, *a, *b, *c; |
void *arg, *a, *b, *c; |
1941 |
* This sort is stable, i.e. sequence of equal elements is preserved. |
* This sort is stable, i.e. sequence of equal elements is preserved. |
1942 |
* See also bug #224128. |
* See also bug #224128. |
1943 |
*/ |
*/ |
1944 |
JS_REQUIRES_STACK JSBool |
JSBool |
1945 |
js_MergeSort(void *src, size_t nel, size_t elsize, |
js_MergeSort(void *src, size_t nel, size_t elsize, |
1946 |
JSComparator cmp, void *arg, void *tmp) |
JSComparator cmp, void *arg, void *tmp) |
1947 |
{ |
{ |
2074 |
return JS_TRUE; |
return JS_TRUE; |
2075 |
} |
} |
2076 |
|
|
2077 |
|
typedef JSBool (JS_REQUIRES_STACK *JSRedComparator)(void*, const void*, |
2078 |
|
const void*, int *); |
2079 |
|
|
2080 |
|
static inline JS_IGNORE_STACK JSComparator |
2081 |
|
comparator_stack_cast(JSRedComparator func) |
2082 |
|
{ |
2083 |
|
return func; |
2084 |
|
} |
2085 |
|
|
2086 |
static int |
static int |
2087 |
sort_compare_strings(void *arg, const void *a, const void *b, int *result) |
sort_compare_strings(void *arg, const void *a, const void *b, int *result) |
2088 |
{ |
{ |
2104 |
*/ |
*/ |
2105 |
JS_STATIC_ASSERT(JSVAL_NULL == 0); |
JS_STATIC_ASSERT(JSVAL_NULL == 0); |
2106 |
|
|
2107 |
static JS_REQUIRES_STACK JSBool |
static JSBool |
2108 |
array_sort(JSContext *cx, uintN argc, jsval *vp) |
array_sort(JSContext *cx, uintN argc, jsval *vp) |
2109 |
{ |
{ |
2110 |
jsval *argv, fval, *vec, *mergesort_tmp, v; |
jsval *argv, fval, *vec, *mergesort_tmp, v; |
2155 |
return JS_FALSE; |
return JS_FALSE; |
2156 |
} |
} |
2157 |
#endif |
#endif |
2158 |
vec = (jsval *) JS_malloc(cx, 2 * (size_t) len * sizeof(jsval)); |
vec = (jsval *) cx->malloc(2 * (size_t) len * sizeof(jsval)); |
2159 |
if (!vec) |
if (!vec) |
2160 |
return JS_FALSE; |
return JS_FALSE; |
2161 |
|
|
2284 |
} while (i != 0); |
} while (i != 0); |
2285 |
|
|
2286 |
JS_ASSERT(tvr.u.array == vec); |
JS_ASSERT(tvr.u.array == vec); |
2287 |
vec = (jsval *) JS_realloc(cx, vec, |
vec = (jsval *) cx->realloc(vec, |
2288 |
4 * (size_t) newlen * sizeof(jsval)); |
4 * (size_t) newlen * sizeof(jsval)); |
2289 |
if (!vec) { |
if (!vec) { |
2290 |
vec = tvr.u.array; |
vec = tvr.u.array; |
2291 |
ok = JS_FALSE; |
ok = JS_FALSE; |
2316 |
} else { |
} else { |
2317 |
void *mark; |
void *mark; |
2318 |
|
|
2319 |
|
js_LeaveTrace(cx); |
2320 |
|
|
2321 |
ca.context = cx; |
ca.context = cx; |
2322 |
ca.fval = fval; |
ca.fval = fval; |
2323 |
ca.elemroot = js_AllocStack(cx, 2 + 2, &mark); |
ca.elemroot = js_AllocStack(cx, 2 + 2, &mark); |
2326 |
goto out; |
goto out; |
2327 |
} |
} |
2328 |
ok = js_MergeSort(vec, (size_t) newlen, sizeof(jsval), |
ok = js_MergeSort(vec, (size_t) newlen, sizeof(jsval), |
2329 |
sort_compare, &ca, mergesort_tmp); |
comparator_stack_cast(sort_compare), |
2330 |
|
&ca, mergesort_tmp); |
2331 |
js_FreeStack(cx, mark); |
js_FreeStack(cx, mark); |
2332 |
if (!ok) |
if (!ok) |
2333 |
goto out; |
goto out; |
2346 |
|
|
2347 |
out: |
out: |
2348 |
JS_POP_TEMP_ROOT(cx, &tvr); |
JS_POP_TEMP_ROOT(cx, &tvr); |
2349 |
JS_free(cx, vec); |
cx->free(vec); |
2350 |
if (!ok) |
if (!ok) |
2351 |
return JS_FALSE; |
return JS_FALSE; |
2352 |
|
|
2420 |
JS_ASSERT(length <= js_DenseArrayCapacity(obj)); |
JS_ASSERT(length <= js_DenseArrayCapacity(obj)); |
2421 |
|
|
2422 |
if (length == js_DenseArrayCapacity(obj)) { |
if (length == js_DenseArrayCapacity(obj)) { |
2423 |
if (length >= ARRAY_INIT_LIMIT) { |
if (length > JS_ARGS_LENGTH_MAX) { |
2424 |
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, |
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, |
2425 |
JSMSG_ARRAY_INIT_TOO_BIG); |
JSMSG_ARRAY_INIT_TOO_BIG); |
2426 |
return JS_FALSE; |
return JS_FALSE; |
2434 |
obj->dslots[length] = v; |
obj->dslots[length] = v; |
2435 |
return JS_TRUE; |
return JS_TRUE; |
2436 |
} |
} |
2437 |
|
JS_DEFINE_CALLINFO_3(extern, BOOL, js_ArrayCompPush, CONTEXT, OBJECT, JSVAL, 0, 0) |
2438 |
|
|
2439 |
#ifdef JS_TRACER |
#ifdef JS_TRACER |
2440 |
static jsval FASTCALL |
static jsval FASTCALL |
2812 |
JSObject *aobj, *nobj; |
JSObject *aobj, *nobj; |
2813 |
jsuint length, alength, slot; |
jsuint length, alength, slot; |
2814 |
uintN i; |
uintN i; |
2815 |
JSBool hole, ok; |
JSBool hole; |
|
JSTempValueRooter tvr; |
|
2816 |
|
|
2817 |
/* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */ |
/* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */ |
2818 |
argv = JS_ARGV(cx, vp) - 1; |
argv = JS_ARGV(cx, vp) - 1; |
2850 |
length = 0; |
length = 0; |
2851 |
} |
} |
2852 |
|
|
2853 |
MUST_FLOW_THROUGH("out"); |
JSAutoTempValueRooter tvr(cx, JSVAL_NULL); |
|
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); |
|
2854 |
|
|
2855 |
/* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */ |
/* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */ |
2856 |
for (i = 0; i <= argc; i++) { |
for (i = 0; i <= argc; i++) { |
2857 |
ok = JS_CHECK_OPERATION_LIMIT(cx); |
if (!JS_CHECK_OPERATION_LIMIT(cx)) |
2858 |
if (!ok) |
return false; |
|
goto out; |
|
2859 |
v = argv[i]; |
v = argv[i]; |
2860 |
if (!JSVAL_IS_PRIMITIVE(v)) { |
if (!JSVAL_IS_PRIMITIVE(v)) { |
2861 |
JSObject *wobj; |
JSObject *wobj; |
2863 |
aobj = JSVAL_TO_OBJECT(v); |
aobj = JSVAL_TO_OBJECT(v); |
2864 |
wobj = js_GetWrappedObject(cx, aobj); |
wobj = js_GetWrappedObject(cx, aobj); |
2865 |
if (OBJ_IS_ARRAY(cx, wobj)) { |
if (OBJ_IS_ARRAY(cx, wobj)) { |
2866 |
ok = OBJ_GET_PROPERTY(cx, aobj, |
jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); |
2867 |
ATOM_TO_JSID(cx->runtime->atomState |
if (!aobj->getProperty(cx, id, tvr.addr())) |
2868 |
.lengthAtom), |
return false; |
2869 |
&tvr.u.value); |
alength = ValueIsLength(cx, tvr.addr()); |
2870 |
if (!ok) |
if (JSVAL_IS_NULL(tvr.value())) |
2871 |
goto out; |
return false; |
|
alength = ValueIsLength(cx, &tvr.u.value); |
|
|
ok = !JSVAL_IS_NULL(tvr.u.value); |
|
|
if (!ok) |
|
|
goto out; |
|
2872 |
for (slot = 0; slot < alength; slot++) { |
for (slot = 0; slot < alength; slot++) { |
2873 |
ok = JS_CHECK_OPERATION_LIMIT(cx) && |
if (!JS_CHECK_OPERATION_LIMIT(cx) || |
2874 |
GetArrayElement(cx, aobj, slot, &hole, |
!GetArrayElement(cx, aobj, slot, &hole, tvr.addr())) { |
2875 |
&tvr.u.value); |
return false; |
2876 |
if (!ok) |
} |
|
goto out; |
|
2877 |
|
|
2878 |
/* |
/* |
2879 |
* Per ECMA 262, 15.4.4.4, step 9, ignore non-existent |
* Per ECMA 262, 15.4.4.4, step 9, ignore non-existent |
2880 |
* properties. |
* properties. |
2881 |
*/ |
*/ |
2882 |
if (!hole) { |
if (!hole && |
2883 |
ok = SetArrayElement(cx, nobj, length + slot, |
!SetArrayElement(cx, nobj, length+slot, tvr.value())) { |
2884 |
tvr.u.value); |
return false; |
|
if (!ok) |
|
|
goto out; |
|
2885 |
} |
} |
2886 |
} |
} |
2887 |
length += alength; |
length += alength; |
2889 |
} |
} |
2890 |
} |
} |
2891 |
|
|
2892 |
ok = SetArrayElement(cx, nobj, length, v); |
if (!SetArrayElement(cx, nobj, length, v)) |
2893 |
if (!ok) |
return false; |
|
goto out; |
|
2894 |
length++; |
length++; |
2895 |
} |
} |
2896 |
|
|
2897 |
ok = js_SetLengthProperty(cx, nobj, length); |
return js_SetLengthProperty(cx, nobj, length); |
|
|
|
|
out: |
|
|
JS_POP_TEMP_ROOT(cx, &tvr); |
|
|
return ok; |
|
2898 |
} |
} |
2899 |
|
|
2900 |
static JSBool |
static JSBool |
3073 |
|
|
3074 |
#define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT) |
#define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT) |
3075 |
|
|
3076 |
static JS_REQUIRES_STACK JSBool |
static JSBool |
3077 |
array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, jsval *vp) |
array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, jsval *vp) |
3078 |
{ |
{ |
3079 |
JSObject *obj; |
JSObject *obj; |
3171 |
* For all but REDUCE, we call with 3 args (value, index, array). REDUCE |
* For all but REDUCE, we call with 3 args (value, index, array). REDUCE |
3172 |
* requires 4 args (accum, value, index, array). |
* requires 4 args (accum, value, index, array). |
3173 |
*/ |
*/ |
3174 |
|
js_LeaveTrace(cx); |
3175 |
argc = 3 + REDUCE_MODE(mode); |
argc = 3 + REDUCE_MODE(mode); |
3176 |
elemroot = js_AllocStack(cx, 1 + 2 + argc, &mark); |
elemroot = js_AllocStack(cx, 1 + 2 + argc, &mark); |
3177 |
if (!elemroot) |
if (!elemroot) |
3258 |
return ok; |
return ok; |
3259 |
} |
} |
3260 |
|
|
3261 |
static JS_REQUIRES_STACK JSBool |
static JSBool |
3262 |
array_forEach(JSContext *cx, uintN argc, jsval *vp) |
array_forEach(JSContext *cx, uintN argc, jsval *vp) |
3263 |
{ |
{ |
3264 |
return array_extra(cx, FOREACH, argc, vp); |
return array_extra(cx, FOREACH, argc, vp); |
3265 |
} |
} |
3266 |
|
|
3267 |
static JS_REQUIRES_STACK JSBool |
static JSBool |
3268 |
array_map(JSContext *cx, uintN argc, jsval *vp) |
array_map(JSContext *cx, uintN argc, jsval *vp) |
3269 |
{ |
{ |
3270 |
return array_extra(cx, MAP, argc, vp); |
return array_extra(cx, MAP, argc, vp); |
3271 |
} |
} |
3272 |
|
|
3273 |
static JS_REQUIRES_STACK JSBool |
static JSBool |
3274 |
array_reduce(JSContext *cx, uintN argc, jsval *vp) |
array_reduce(JSContext *cx, uintN argc, jsval *vp) |
3275 |
{ |
{ |
3276 |
return array_extra(cx, REDUCE, argc, vp); |
return array_extra(cx, REDUCE, argc, vp); |
3277 |
} |
} |
3278 |
|
|
3279 |
static JS_REQUIRES_STACK JSBool |
static JSBool |
3280 |
array_reduceRight(JSContext *cx, uintN argc, jsval *vp) |
array_reduceRight(JSContext *cx, uintN argc, jsval *vp) |
3281 |
{ |
{ |
3282 |
return array_extra(cx, REDUCE_RIGHT, argc, vp); |
return array_extra(cx, REDUCE_RIGHT, argc, vp); |
3283 |
} |
} |
3284 |
|
|
3285 |
static JS_REQUIRES_STACK JSBool |
static JSBool |
3286 |
array_filter(JSContext *cx, uintN argc, jsval *vp) |
array_filter(JSContext *cx, uintN argc, jsval *vp) |
3287 |
{ |
{ |
3288 |
return array_extra(cx, FILTER, argc, vp); |
return array_extra(cx, FILTER, argc, vp); |
3289 |
} |
} |
3290 |
|
|
3291 |
static JS_REQUIRES_STACK JSBool |
static JSBool |
3292 |
array_some(JSContext *cx, uintN argc, jsval *vp) |
array_some(JSContext *cx, uintN argc, jsval *vp) |
3293 |
{ |
{ |
3294 |
return array_extra(cx, SOME, argc, vp); |
return array_extra(cx, SOME, argc, vp); |
3295 |
} |
} |
3296 |
|
|
3297 |
static JS_REQUIRES_STACK JSBool |
static JSBool |
3298 |
array_every(JSContext *cx, uintN argc, jsval *vp) |
array_every(JSContext *cx, uintN argc, jsval *vp) |
3299 |
{ |
{ |
3300 |
return array_extra(cx, EVERY, argc, vp); |
return array_extra(cx, EVERY, argc, vp); |
3320 |
#if JS_HAS_TOSOURCE |
#if JS_HAS_TOSOURCE |
3321 |
JS_FN(js_toSource_str, array_toSource, 0,0), |
JS_FN(js_toSource_str, array_toSource, 0,0), |
3322 |
#endif |
#endif |
3323 |
JS_TN(js_toString_str, array_toString, 0,0, array_toString_trcinfo), |
JS_TN(js_toString_str, array_toString, 0,0, &array_toString_trcinfo), |
3324 |
JS_FN(js_toLocaleString_str,array_toLocaleString,0,0), |
JS_FN(js_toLocaleString_str,array_toLocaleString,0,0), |
3325 |
|
|
3326 |
/* Perl-ish methods. */ |
/* Perl-ish methods. */ |
3327 |
JS_TN("join", array_join, 1,JSFUN_GENERIC_NATIVE, array_join_trcinfo), |
JS_TN("join", array_join, 1,JSFUN_GENERIC_NATIVE, &array_join_trcinfo), |
3328 |
JS_FN("reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE), |
JS_FN("reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE), |
3329 |
JS_FN("sort", array_sort, 1,JSFUN_GENERIC_NATIVE), |
JS_FN("sort", array_sort, 1,JSFUN_GENERIC_NATIVE), |
3330 |
JS_TN("push", array_push, 1,JSFUN_GENERIC_NATIVE, array_push_trcinfo), |
JS_TN("push", array_push, 1,JSFUN_GENERIC_NATIVE, &array_push_trcinfo), |
3331 |
JS_TN("pop", array_pop, 0,JSFUN_GENERIC_NATIVE, array_pop_trcinfo), |
JS_TN("pop", array_pop, 0,JSFUN_GENERIC_NATIVE, &array_pop_trcinfo), |
3332 |
JS_FN("shift", array_shift, 0,JSFUN_GENERIC_NATIVE), |
JS_FN("shift", array_shift, 0,JSFUN_GENERIC_NATIVE), |
3333 |
JS_FN("unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE), |
JS_FN("unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE), |
3334 |
JS_FN("splice", array_splice, 2,JSFUN_GENERIC_NATIVE), |
JS_FN("splice", array_splice, 2,JSFUN_GENERIC_NATIVE), |
3360 |
|
|
3361 |
/* If called without new, replace obj with a new Array object. */ |
/* If called without new, replace obj with a new Array object. */ |
3362 |
if (!JS_IsConstructing(cx)) { |
if (!JS_IsConstructing(cx)) { |
3363 |
obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL, 0); |
obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); |
3364 |
if (!obj) |
if (!obj) |
3365 |
return JS_FALSE; |
return JS_FALSE; |
3366 |
*rval = OBJECT_TO_JSVAL(obj); |
*rval = OBJECT_TO_JSVAL(obj); |
3395 |
JS_ASSERT(OBJ_IS_ARRAY(cx, proto)); |
JS_ASSERT(OBJ_IS_ARRAY(cx, proto)); |
3396 |
|
|
3397 |
JS_ASSERT(JS_ON_TRACE(cx)); |
JS_ASSERT(JS_ON_TRACE(cx)); |
3398 |
JSObject* obj = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); |
JSObject* obj = js_NewGCObject(cx, GCX_OBJECT); |
3399 |
if (!obj) |
if (!obj) |
3400 |
return NULL; |
return NULL; |
3401 |
|
|
3402 |
/* Initialize all fields of JSObject. */ |
/* Initialize all fields of JSObject. */ |
3403 |
obj->map = const_cast<JSObjectMap *>(&SharedArrayMap); |
obj->map = const_cast<JSObjectMap *>(&SharedArrayMap); |
3404 |
obj->classword = jsuword(&js_ArrayClass); |
obj->classword = jsuword(&js_ArrayClass); |
3405 |
obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); |
obj->setProto(proto); |
3406 |
obj->fslots[JSSLOT_PARENT] = proto->fslots[JSSLOT_PARENT]; |
obj->setParent(proto->getParent()); |
3407 |
|
|
3408 |
obj->fslots[JSSLOT_ARRAY_LENGTH] = 0; |
obj->fslots[JSSLOT_ARRAY_LENGTH] = 0; |
3409 |
obj->fslots[JSSLOT_ARRAY_COUNT] = 0; |
obj->fslots[JSSLOT_ARRAY_COUNT] = 0; |
3412 |
obj->dslots = NULL; |
obj->dslots = NULL; |
3413 |
return obj; |
return obj; |
3414 |
} |
} |
3415 |
|
JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, 0, 0) |
3416 |
|
|
3417 |
|
JSObject* JS_FASTCALL |
3418 |
|
js_NewEmptyArrayWithLength(JSContext* cx, JSObject* proto, int32 len) |
3419 |
|
{ |
3420 |
|
if (len < 0) |
3421 |
|
return NULL; |
3422 |
|
JSObject *obj = js_NewEmptyArray(cx, proto); |
3423 |
|
if (!obj) |
3424 |
|
return NULL; |
3425 |
|
obj->fslots[JSSLOT_ARRAY_LENGTH] = len; |
3426 |
|
return obj; |
3427 |
|
} |
3428 |
|
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewEmptyArrayWithLength, CONTEXT, OBJECT, INT32, 0, 0) |
3429 |
|
|
3430 |
JSObject* FASTCALL |
JSObject* FASTCALL |
3431 |
js_NewUninitializedArray(JSContext* cx, JSObject* proto, uint32 len) |
js_NewUninitializedArray(JSContext* cx, JSObject* proto, uint32 len) |
3439 |
return NULL; |
return NULL; |
3440 |
return obj; |
return obj; |
3441 |
} |
} |
3442 |
|
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewUninitializedArray, CONTEXT, OBJECT, UINT32, 0, 0) |
3443 |
|
|
3444 |
#endif /* JS_TRACER */ |
#endif /* JS_TRACER */ |
3445 |
|
|
3469 |
JSTempValueRooter tvr; |
JSTempValueRooter tvr; |
3470 |
JSObject *obj; |
JSObject *obj; |
3471 |
|
|
3472 |
obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL, 0); |
obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); |
3473 |
if (!obj) |
if (!obj) |
3474 |
return NULL; |
return NULL; |
3475 |
|
|
3476 |
|
/* |
3477 |
|
* If this fails, the global object was not initialized and its class does |
3478 |
|
* not have JSCLASS_IS_GLOBAL. |
3479 |
|
*/ |
3480 |
|
JS_ASSERT(obj->getProto()); |
3481 |
|
|
3482 |
JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); |
JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); |
3483 |
if (!InitArrayObject(cx, obj, length, vector, holey)) |
if (!InitArrayObject(cx, obj, length, vector, holey)) |
3484 |
obj = NULL; |
obj = NULL; |
3492 |
JSObject * |
JSObject * |
3493 |
js_NewSlowArrayObject(JSContext *cx) |
js_NewSlowArrayObject(JSContext *cx) |
3494 |
{ |
{ |
3495 |
JSObject *obj = js_NewObject(cx, &js_SlowArrayClass, NULL, NULL, 0); |
JSObject *obj = js_NewObject(cx, &js_SlowArrayClass, NULL, NULL); |
3496 |
if (obj) |
if (obj) |
3497 |
obj->fslots[JSSLOT_ARRAY_LENGTH] = 0; |
obj->fslots[JSSLOT_ARRAY_LENGTH] = 0; |
3498 |
return obj; |
return obj; |
3515 |
if (JSVAL_IS_PRIMITIVE(argv[i]) || |
if (JSVAL_IS_PRIMITIVE(argv[i]) || |
3516 |
!OBJ_IS_ARRAY(cx, (array = JSVAL_TO_OBJECT(argv[i])))) { |
!OBJ_IS_ARRAY(cx, (array = JSVAL_TO_OBJECT(argv[i])))) { |
3517 |
fprintf(stderr, "%s: not array\n", bytes); |
fprintf(stderr, "%s: not array\n", bytes); |
3518 |
JS_free(cx, bytes); |
cx->free(bytes); |
3519 |
continue; |
continue; |
3520 |
} |
} |
3521 |
fprintf(stderr, "%s: %s (len %lu", bytes, |
fprintf(stderr, "%s: %s (len %lu", bytes, |
3527 |
js_DenseArrayCapacity(array)); |
js_DenseArrayCapacity(array)); |
3528 |
} |
} |
3529 |
fputs(")\n", stderr); |
fputs(")\n", stderr); |
3530 |
JS_free(cx, bytes); |
cx->free(bytes); |
3531 |
} |
} |
3532 |
return JS_TRUE; |
return JS_TRUE; |
3533 |
} |
} |
3534 |
#endif |
#endif |
3535 |
|
|
3536 |
JS_FRIEND_API(JSBool) |
JS_FRIEND_API(JSBool) |
3537 |
js_ArrayToJSUint8Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count, |
js_CoerceArrayToCanvasImageData(JSObject *obj, jsuint offset, jsuint count, |
3538 |
JSUint8 *dest) |
JSUint8 *dest) |
3539 |
{ |
{ |
3540 |
uint32 length; |
uint32 length; |
3541 |
|
|
3542 |
if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj)) |
if (!obj || !js_IsDenseArray(obj)) |
3543 |
return JS_FALSE; |
return JS_FALSE; |
3544 |
|
|
3545 |
length = obj->fslots[JSSLOT_ARRAY_LENGTH]; |
length = obj->fslots[JSSLOT_ARRAY_LENGTH]; |
3546 |
if (length < offset + count) |
if (length < offset + count) |
3547 |
return JS_FALSE; |
return JS_FALSE; |
3548 |
|
|
|
jsval v; |
|
|
jsint vi; |
|
|
|
|
3549 |
JSUint8 *dp = dest; |
JSUint8 *dp = dest; |
3550 |
for (uintN i = offset; i < offset+count; i++) { |
for (uintN i = offset; i < offset+count; i++) { |
3551 |
v = obj->dslots[i]; |
jsval v = obj->dslots[i]; |
3552 |
if (!JSVAL_IS_INT(v) || (vi = JSVAL_TO_INT(v)) < 0) |
if (JSVAL_IS_INT(v)) { |
3553 |
return JS_FALSE; |
jsint vi = JSVAL_TO_INT(v); |
3554 |
|
if (jsuint(vi) > 255) |
3555 |
*dp++ = (JSUint8) vi; |
vi = (vi < 0) ? 0 : 255; |
3556 |
} |
*dp++ = JSUint8(vi); |
3557 |
|
} else if (JSVAL_IS_DOUBLE(v)) { |
3558 |
return JS_TRUE; |
jsdouble vd = *JSVAL_TO_DOUBLE(v); |
3559 |
} |
if (!(vd >= 0)) /* Not < so that NaN coerces to 0 */ |
3560 |
|
*dp++ = 0; |
3561 |
JS_FRIEND_API(JSBool) |
else if (vd > 255) |
3562 |
js_ArrayToJSUint16Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count, |
*dp++ = 255; |
3563 |
JSUint16 *dest) |
else { |
3564 |
{ |
jsdouble toTruncate = vd + 0.5; |
3565 |
uint32 length; |
JSUint8 val = JSUint8(toTruncate); |
|
|
|
|
if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj)) |
|
|
return JS_FALSE; |
|
|
|
|
|
length = obj->fslots[JSSLOT_ARRAY_LENGTH]; |
|
|
if (length < offset + count) |
|
|
return JS_FALSE; |
|
|
|
|
|
jsval v; |
|
|
jsint vi; |
|
|
|
|
|
JSUint16 *dp = dest; |
|
|
for (uintN i = offset; i < offset+count; i++) { |
|
|
v = obj->dslots[i]; |
|
|
if (!JSVAL_IS_INT(v) || (vi = JSVAL_TO_INT(v)) < 0) |
|
|
return JS_FALSE; |
|
|
|
|
|
*dp++ = (JSUint16) vi; |
|
|
} |
|
|
|
|
|
return JS_TRUE; |
|
|
} |
|
|
|
|
|
JS_FRIEND_API(JSBool) |
|
|
js_ArrayToJSUint32Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count, |
|
|
JSUint32 *dest) |
|
|
{ |
|
|
uint32 length; |
|
|
|
|
|
if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj)) |
|
|
return JS_FALSE; |
|
|
|
|
|
length = obj->fslots[JSSLOT_ARRAY_LENGTH]; |
|
|
if (length < offset + count) |
|
|
return JS_FALSE; |
|
|
|
|
|
jsval v; |
|
|
jsint vi; |
|
|
|
|
|
JSUint32 *dp = dest; |
|
|
for (uintN i = offset; i < offset+count; i++) { |
|
|
v = obj->dslots[i]; |
|
|
if (!JSVAL_IS_INT(v) || (vi = JSVAL_TO_INT(v)) < 0) |
|
|
return JS_FALSE; |
|
|
|
|
|
*dp++ = (JSUint32) vi; |
|
|
} |
|
|
|
|
|
return JS_TRUE; |
|
|
} |
|
|
|
|
|
JS_FRIEND_API(JSBool) |
|
|
js_ArrayToJSInt8Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count, |
|
|
JSInt8 *dest) |
|
|
{ |
|
|
uint32 length; |
|
|
|
|
|
if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj)) |
|
|
return JS_FALSE; |
|
|
|
|
|
length = obj->fslots[JSSLOT_ARRAY_LENGTH]; |
|
|
if (length < offset + count) |
|
|
return JS_FALSE; |
|
|
|
|
|
jsval v; |
|
|
JSInt8 *dp = dest; |
|
|
for (uintN i = offset; i < offset+count; i++) { |
|
|
v = obj->dslots[i]; |
|
|
if (!JSVAL_IS_INT(v)) |
|
|
return JS_FALSE; |
|
|
|
|
|
*dp++ = (JSInt8) JSVAL_TO_INT(v); |
|
|
} |
|
|
|
|
|
return JS_TRUE; |
|
|
} |
|
|
|
|
|
JS_FRIEND_API(JSBool) |
|
|
js_ArrayToJSInt16Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count, |
|
|
JSInt16 *dest) |
|
|
{ |
|
|
uint32 length; |
|
|
|
|
|
if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj)) |
|
|
return JS_FALSE; |
|
|
|
|
|
length = obj->fslots[JSSLOT_ARRAY_LENGTH]; |
|
|
if (length < offset + count) |
|
|
return JS_FALSE; |
|
|
|
|
|
jsval v; |
|
|
JSInt16 *dp = dest; |
|
|
for (uintN i = offset; i < offset+count; i++) { |
|
|
v = obj->dslots[i]; |
|
|
if (!JSVAL_IS_INT(v)) |
|
|
return JS_FALSE; |
|
|
|
|
|
*dp++ = (JSInt16) JSVAL_TO_INT(v); |
|
|
} |
|
|
|
|
|
return JS_TRUE; |
|
|
} |
|
|
|
|
|
JS_FRIEND_API(JSBool) |
|
|
js_ArrayToJSInt32Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count, |
|
|
JSInt32 *dest) |
|
|
{ |
|
|
uint32 length; |
|
3566 |
|
|
3567 |
if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj)) |
/* |
3568 |
return JS_FALSE; |
* now val is rounded to nearest, ties rounded up. We want |
3569 |
|
* rounded to nearest ties to even, so check whether we had a |
3570 |
length = obj->fslots[JSSLOT_ARRAY_LENGTH]; |
* tie. |
3571 |
if (length < offset + count) |
*/ |
3572 |
return JS_FALSE; |
if (val == toTruncate) { |
3573 |
|
/* |
3574 |
jsval v; |
* It was a tie (since adding 0.5 gave us the exact integer |
3575 |
JSInt32 *dp = dest; |
* we want). Since we rounded up, we either already have an |
3576 |
for (uintN i = offset; i < offset+count; i++) { |
* even number or we have an odd number but the number we |
3577 |
v = obj->dslots[i]; |
* want is one less. So just unconditionally masking out the |
3578 |
if (!JSVAL_IS_INT(v)) |
* ones bit should do the trick to get us the value we |
3579 |
|
* want. |
3580 |
|
*/ |
3581 |
|
*dp++ = (val & ~1); |
3582 |
|
} else { |
3583 |
|
*dp++ = val; |
3584 |
|
} |
3585 |
|
} |
3586 |
|
} else { |
3587 |
return JS_FALSE; |
return JS_FALSE; |
3588 |
|
} |
|
*dp++ = (JSInt32) JSVAL_TO_INT(v); |
|
3589 |
} |
} |
3590 |
|
|
3591 |
return JS_TRUE; |
return JS_TRUE; |
3592 |
} |
} |
3593 |
|
|
3594 |
JS_FRIEND_API(JSBool) |
JS_FRIEND_API(JSObject *) |
3595 |
js_ArrayToJSDoubleBuffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count, |
js_NewArrayObjectWithCapacity(JSContext *cx, jsuint capacity, jsval **vector) |
|
jsdouble *dest) |
|
3596 |
{ |
{ |
3597 |
uint32 length; |
JSObject *obj = js_NewArrayObject(cx, capacity, NULL); |
3598 |
|
if (!obj) |
3599 |
if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj)) |
return NULL; |
|
return JS_FALSE; |
|
3600 |
|
|
3601 |
length = obj->fslots[JSSLOT_ARRAY_LENGTH]; |
JSAutoTempValueRooter tvr(cx, obj); |
3602 |
if (length < offset + count) |
if (!EnsureCapacity(cx, obj, capacity, JS_FALSE)) |
3603 |
return JS_FALSE; |
obj = NULL; |
3604 |
|
|
3605 |
jsval v; |
/* Set/clear newborn root, in case we lost it. */ |
3606 |
jsdouble *dp = dest; |
cx->weakRoots.newborn[GCX_OBJECT] = obj; |
3607 |
for (uintN i = offset; i < offset+count; i++) { |
if (!obj) |
3608 |
v = obj->dslots[i]; |
return NULL; |
|
if (JSVAL_IS_INT(v)) |
|
|
*dp++ = (jsdouble) JSVAL_TO_INT(v); |
|
|
else if (JSVAL_IS_DOUBLE(v)) |
|
|
*dp++ = *(JSVAL_TO_DOUBLE(v)); |
|
|
else |
|
|
return JS_FALSE; |
|
|
} |
|
3609 |
|
|
3610 |
return JS_TRUE; |
obj->fslots[JSSLOT_ARRAY_COUNT] = capacity; |
3611 |
|
*vector = obj->dslots; |
3612 |
|
return obj; |
3613 |
} |
} |
|
|
|
|
JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem, CONTEXT, OBJECT, INT32, JSVAL, 0, 0) |
|
|
JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, 0, 0) |
|
|
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewUninitializedArray, CONTEXT, OBJECT, UINT32, 0, 0) |
|
|
JS_DEFINE_CALLINFO_3(extern, BOOL, js_ArrayCompPush, CONTEXT, OBJECT, JSVAL, 0, 0) |
|