1 |
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 |
|
* vim: set sw=4 ts=8 et tw=80: |
3 |
|
* |
4 |
|
* ***** BEGIN LICENSE BLOCK ***** |
5 |
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
6 |
|
* |
7 |
|
* The contents of this file are subject to the Mozilla Public License Version |
8 |
|
* 1.1 (the "License"); you may not use this file except in compliance with |
9 |
|
* the License. You may obtain a copy of the License at |
10 |
|
* http://www.mozilla.org/MPL/ |
11 |
|
* |
12 |
|
* Software distributed under the License is distributed on an "AS IS" basis, |
13 |
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
14 |
|
* for the specific language governing rights and limitations under the |
15 |
|
* License. |
16 |
|
* |
17 |
|
* The Original Code is Mozilla Communicator client code, released |
18 |
|
* March 31, 1998. |
19 |
|
* |
20 |
|
* The Initial Developer of the Original Code is |
21 |
|
* Netscape Communications Corporation. |
22 |
|
* Portions created by the Initial Developer are Copyright (C) 1998 |
23 |
|
* the Initial Developer. All Rights Reserved. |
24 |
|
* |
25 |
|
* Contributor(s): |
26 |
|
* |
27 |
|
* Alternatively, the contents of this file may be used under the terms of |
28 |
|
* either of the GNU General Public License Version 2 or later (the "GPL"), |
29 |
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
30 |
|
* in which case the provisions of the GPL or the LGPL are applicable instead |
31 |
|
* of those above. If you wish to allow use of your version of this file only |
32 |
|
* under the terms of either the GPL or the LGPL, and not to allow others to |
33 |
|
* use your version of this file under the terms of the MPL, indicate your |
34 |
|
* decision by deleting the provisions above and replace them with the notice |
35 |
|
* and other provisions required by the GPL or the LGPL. If you do not delete |
36 |
|
* the provisions above, a recipient may use your version of this file under |
37 |
|
* the terms of any one of the MPL, the GPL or the LGPL. |
38 |
|
* |
39 |
|
* ***** END LICENSE BLOCK ***** */ |
40 |
|
|
41 |
|
/* |
42 |
|
* JS array class. |
43 |
|
*/ |
44 |
|
#include "jsstddef.h" |
45 |
|
#include <stdlib.h> |
46 |
|
#include <string.h> |
47 |
|
#include "jstypes.h" |
48 |
|
#include "jsutil.h" /* Added by JSIFY */ |
49 |
|
#include "jsapi.h" |
50 |
|
#include "jsarray.h" |
51 |
|
#include "jsatom.h" |
52 |
|
#include "jsbool.h" |
53 |
|
#include "jscntxt.h" |
54 |
|
#include "jsconfig.h" |
55 |
|
#include "jsfun.h" |
56 |
|
#include "jsgc.h" |
57 |
|
#include "jsinterp.h" |
58 |
|
#include "jslock.h" |
59 |
|
#include "jsnum.h" |
60 |
|
#include "jsobj.h" |
61 |
|
#include "jsstr.h" |
62 |
|
|
63 |
|
/* 2^32 - 1 as a number and a string */ |
64 |
|
#define MAXINDEX 4294967295u |
65 |
|
#define MAXSTR "4294967295" |
66 |
|
|
67 |
|
/* |
68 |
|
* Determine if the id represents an array index or an XML property index. |
69 |
|
* |
70 |
|
* An id is an array index according to ECMA by (15.4): |
71 |
|
* |
72 |
|
* "Array objects give special treatment to a certain class of property names. |
73 |
|
* A property name P (in the form of a string value) is an array index if and |
74 |
|
* only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal |
75 |
|
* to 2^32-1." |
76 |
|
* |
77 |
|
* In our implementation, it would be sufficient to check for JSVAL_IS_INT(id) |
78 |
|
* except that by using signed 32-bit integers we miss the top half of the |
79 |
|
* valid range. This function checks the string representation itself; note |
80 |
|
* that calling a standard conversion routine might allow strings such as |
81 |
|
* "08" or "4.0" as array indices, which they are not. |
82 |
|
*/ |
83 |
|
JSBool |
84 |
|
js_IdIsIndex(jsval id, jsuint *indexp) |
85 |
171071 |
{ |
86 |
171071 |
JSString *str; |
87 |
171071 |
jschar *cp; |
88 |
|
|
89 |
171071 |
if (JSVAL_IS_INT(id)) { |
90 |
101812 |
jsint i; |
91 |
101812 |
i = JSVAL_TO_INT(id); |
92 |
101812 |
if (i < 0) |
93 |
0 |
return JS_FALSE; |
94 |
101812 |
*indexp = (jsuint)i; |
95 |
101812 |
return JS_TRUE; |
96 |
|
} |
97 |
|
|
98 |
|
/* NB: id should be a string, but jsxml.c may call us with an object id. */ |
99 |
69259 |
if (!JSVAL_IS_STRING(id)) |
100 |
0 |
return JS_FALSE; |
101 |
|
|
102 |
69259 |
str = JSVAL_TO_STRING(id); |
103 |
69259 |
cp = JSSTRING_CHARS(str); |
104 |
69259 |
if (JS7_ISDEC(*cp) && JSSTRING_LENGTH(str) < sizeof(MAXSTR)) { |
105 |
0 |
jsuint index = JS7_UNDEC(*cp++); |
106 |
0 |
jsuint oldIndex = 0; |
107 |
0 |
jsuint c = 0; |
108 |
0 |
if (index != 0) { |
109 |
0 |
while (JS7_ISDEC(*cp)) { |
110 |
0 |
oldIndex = index; |
111 |
0 |
c = JS7_UNDEC(*cp); |
112 |
0 |
index = 10*index + c; |
113 |
0 |
cp++; |
114 |
|
} |
115 |
|
} |
116 |
|
|
117 |
|
/* Ensure that all characters were consumed and we didn't overflow. */ |
118 |
0 |
if (*cp == 0 && |
119 |
|
(oldIndex < (MAXINDEX / 10) || |
120 |
|
(oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10)))) |
121 |
|
{ |
122 |
0 |
*indexp = index; |
123 |
0 |
return JS_TRUE; |
124 |
|
} |
125 |
|
} |
126 |
69259 |
return JS_FALSE; |
127 |
|
} |
128 |
|
|
129 |
|
static JSBool |
130 |
|
ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp) |
131 |
111840 |
{ |
132 |
111840 |
jsint i; |
133 |
111840 |
jsdouble d; |
134 |
|
|
135 |
111840 |
if (JSVAL_IS_INT(v)) { |
136 |
111840 |
i = JSVAL_TO_INT(v); |
137 |
111840 |
if (i < 0) { |
138 |
0 |
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
139 |
|
JSMSG_BAD_ARRAY_LENGTH); |
140 |
0 |
return JS_FALSE; |
141 |
|
} |
142 |
111840 |
*lengthp = (jsuint) i; |
143 |
111840 |
return JS_TRUE; |
144 |
|
} |
145 |
|
|
146 |
0 |
if (!js_ValueToNumber(cx, v, &d)) { |
147 |
0 |
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
148 |
|
JSMSG_BAD_ARRAY_LENGTH); |
149 |
0 |
return JS_FALSE; |
150 |
|
} |
151 |
0 |
if (!js_DoubleToECMAUint32(cx, d, (uint32 *)lengthp)) { |
152 |
0 |
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
153 |
|
JSMSG_BAD_ARRAY_LENGTH); |
154 |
0 |
return JS_FALSE; |
155 |
|
} |
156 |
0 |
if (JSDOUBLE_IS_NaN(d) || d != *lengthp) { |
157 |
0 |
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
158 |
|
JSMSG_BAD_ARRAY_LENGTH); |
159 |
0 |
return JS_FALSE; |
160 |
|
} |
161 |
0 |
return JS_TRUE; |
162 |
|
} |
163 |
|
|
164 |
|
JSBool |
165 |
|
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) |
166 |
224807 |
{ |
167 |
224807 |
JSTempValueRooter tvr; |
168 |
224807 |
jsid id; |
169 |
224807 |
JSBool ok; |
170 |
224807 |
jsint i; |
171 |
|
|
172 |
224807 |
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); |
173 |
224807 |
id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); |
174 |
224807 |
ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value); |
175 |
224807 |
if (ok) { |
176 |
|
/* |
177 |
|
* Short-circuit, because js_ValueToECMAUint32 fails when called |
178 |
|
* during init time. |
179 |
|
*/ |
180 |
224807 |
if (JSVAL_IS_INT(tvr.u.value)) { |
181 |
224807 |
i = JSVAL_TO_INT(tvr.u.value); |
182 |
224807 |
*lengthp = (jsuint)i; /* jsuint cast does ToUint32 */ |
183 |
|
} else { |
184 |
0 |
ok = js_ValueToECMAUint32(cx, tvr.u.value, (uint32 *)lengthp); |
185 |
|
} |
186 |
|
} |
187 |
224807 |
JS_POP_TEMP_ROOT(cx, &tvr); |
188 |
224807 |
return ok; |
189 |
|
} |
190 |
|
|
191 |
|
static JSBool |
192 |
|
IndexToValue(JSContext *cx, jsuint index, jsval *vp) |
193 |
278198 |
{ |
194 |
278198 |
if (index <= JSVAL_INT_MAX) { |
195 |
278198 |
*vp = INT_TO_JSVAL(index); |
196 |
278198 |
return JS_TRUE; |
197 |
|
} |
198 |
0 |
return js_NewDoubleValue(cx, (jsdouble)index, vp); |
199 |
|
} |
200 |
|
|
201 |
|
static JSBool |
202 |
|
IndexToId(JSContext *cx, jsuint index, jsid *idp) |
203 |
11544 |
{ |
204 |
11544 |
JSString *str; |
205 |
11544 |
JSAtom *atom; |
206 |
|
|
207 |
11544 |
if (index <= JSVAL_INT_MAX) { |
208 |
11544 |
*idp = INT_TO_JSID(index); |
209 |
|
} else { |
210 |
0 |
str = js_NumberToString(cx, (jsdouble)index); |
211 |
0 |
if (!str) |
212 |
0 |
return JS_FALSE; |
213 |
0 |
atom = js_AtomizeString(cx, str, 0); |
214 |
0 |
if (!atom) |
215 |
0 |
return JS_FALSE; |
216 |
0 |
*idp = ATOM_TO_JSID(atom); |
217 |
|
|
218 |
|
} |
219 |
11544 |
return JS_TRUE; |
220 |
|
} |
221 |
|
|
222 |
|
static JSBool |
223 |
|
PropertyExists(JSContext *cx, JSObject *obj, jsid id, JSBool *foundp) |
224 |
1030 |
{ |
225 |
1030 |
JSObject *obj2; |
226 |
1030 |
JSProperty *prop; |
227 |
|
|
228 |
1030 |
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) |
229 |
0 |
return JS_FALSE; |
230 |
|
|
231 |
1030 |
*foundp = prop != NULL; |
232 |
1030 |
if (*foundp) { |
233 |
1030 |
OBJ_DROP_PROPERTY(cx, obj2, prop); |
234 |
|
} |
235 |
|
|
236 |
1030 |
return JS_TRUE; |
237 |
|
} |
238 |
|
|
239 |
|
#define JSID_HOLE JSVAL_NULL |
240 |
|
|
241 |
|
static JSBool |
242 |
|
IndexToExistingId(JSContext *cx, JSObject *obj, jsuint index, jsid *idp) |
243 |
486 |
{ |
244 |
486 |
JSBool exists; |
245 |
|
|
246 |
486 |
if (!IndexToId(cx, index, idp)) |
247 |
0 |
return JS_FALSE; |
248 |
486 |
if (!PropertyExists(cx, obj, *idp, &exists)) |
249 |
0 |
return JS_FALSE; |
250 |
486 |
if (!exists) |
251 |
0 |
*idp = JSID_HOLE; |
252 |
486 |
return JS_TRUE; |
253 |
|
} |
254 |
|
|
255 |
|
JSBool |
256 |
|
js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length) |
257 |
111840 |
{ |
258 |
111840 |
jsval v; |
259 |
111840 |
jsid id; |
260 |
|
|
261 |
111840 |
if (!IndexToValue(cx, length, &v)) |
262 |
0 |
return JS_FALSE; |
263 |
111840 |
id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); |
264 |
111840 |
return OBJ_SET_PROPERTY(cx, obj, id, &v); |
265 |
|
} |
266 |
|
|
267 |
|
JSBool |
268 |
|
js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) |
269 |
0 |
{ |
270 |
0 |
JSErrorReporter older; |
271 |
0 |
JSTempValueRooter tvr; |
272 |
0 |
jsid id; |
273 |
0 |
JSBool ok; |
274 |
|
|
275 |
0 |
older = JS_SetErrorReporter(cx, NULL); |
276 |
0 |
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); |
277 |
0 |
id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); |
278 |
0 |
ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value); |
279 |
0 |
JS_SetErrorReporter(cx, older); |
280 |
0 |
if (ok) |
281 |
0 |
ok = ValueIsLength(cx, tvr.u.value, lengthp); |
282 |
0 |
JS_POP_TEMP_ROOT(cx, &tvr); |
283 |
0 |
return ok; |
284 |
|
} |
285 |
|
|
286 |
|
/* |
287 |
|
* This get function is specific to Array.prototype.length and other array |
288 |
|
* instance length properties. It calls back through the class get function |
289 |
|
* in case some magic happens there (see call_getProperty in jsfun.c). |
290 |
|
*/ |
291 |
|
static JSBool |
292 |
|
array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) |
293 |
236445 |
{ |
294 |
236445 |
return OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, id, vp); |
295 |
|
} |
296 |
|
|
297 |
|
static JSBool |
298 |
|
array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) |
299 |
111840 |
{ |
300 |
111840 |
jsuint newlen, oldlen, slot; |
301 |
111840 |
jsid id2; |
302 |
111840 |
jsval junk; |
303 |
|
|
304 |
111840 |
if (!ValueIsLength(cx, *vp, &newlen)) |
305 |
0 |
return JS_FALSE; |
306 |
111840 |
if (!js_GetLengthProperty(cx, obj, &oldlen)) |
307 |
0 |
return JS_FALSE; |
308 |
111840 |
slot = oldlen; |
309 |
111840 |
while (slot > newlen) { |
310 |
0 |
--slot; |
311 |
0 |
if (!IndexToId(cx, slot, &id2)) |
312 |
0 |
return JS_FALSE; |
313 |
0 |
if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk)) |
314 |
0 |
return JS_FALSE; |
315 |
|
} |
316 |
111840 |
return IndexToValue(cx, newlen, vp); |
317 |
|
} |
318 |
|
|
319 |
|
static JSBool |
320 |
|
array_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) |
321 |
171071 |
{ |
322 |
171071 |
jsuint index, length; |
323 |
|
|
324 |
171071 |
if (!js_IdIsIndex(id, &index)) |
325 |
69259 |
return JS_TRUE; |
326 |
101812 |
if (!js_GetLengthProperty(cx, obj, &length)) |
327 |
0 |
return JS_FALSE; |
328 |
101812 |
if (index >= length) { |
329 |
101812 |
length = index + 1; |
330 |
101812 |
return js_SetLengthProperty(cx, obj, length); |
331 |
|
} |
332 |
0 |
return JS_TRUE; |
333 |
|
} |
334 |
|
|
335 |
|
static JSBool |
336 |
|
array_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) |
337 |
0 |
{ |
338 |
0 |
jsuint length; |
339 |
|
|
340 |
0 |
if (JS_VERSION_IS_1_2(cx)) { |
341 |
0 |
if (!js_GetLengthProperty(cx, obj, &length)) |
342 |
0 |
return JS_FALSE; |
343 |
0 |
switch (type) { |
344 |
|
case JSTYPE_NUMBER: |
345 |
0 |
return IndexToValue(cx, length, vp); |
346 |
|
case JSTYPE_BOOLEAN: |
347 |
0 |
*vp = BOOLEAN_TO_JSVAL(length > 0); |
348 |
0 |
return JS_TRUE; |
349 |
|
default: |
350 |
0 |
return JS_TRUE; |
351 |
|
} |
352 |
|
} |
353 |
0 |
return js_TryValueOf(cx, obj, type, vp); |
354 |
|
} |
355 |
|
|
356 |
|
JSClass js_ArrayClass = { |
357 |
|
"Array", |
358 |
|
0, |
359 |
|
array_addProperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, |
360 |
|
JS_EnumerateStub, JS_ResolveStub, array_convert, JS_FinalizeStub, |
361 |
|
JSCLASS_NO_OPTIONAL_MEMBERS |
362 |
|
}; |
363 |
|
|
364 |
|
static JSBool |
365 |
|
array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize, |
366 |
|
jsval *rval, JSBool localeString) |
367 |
0 |
{ |
368 |
0 |
JSBool ok; |
369 |
0 |
jsuint length, index; |
370 |
0 |
jschar *chars, *ochars; |
371 |
0 |
size_t nchars, growth, seplen, tmplen; |
372 |
0 |
const jschar *sepstr; |
373 |
0 |
JSString *str; |
374 |
0 |
JSHashEntry *he; |
375 |
0 |
JSTempValueRooter tvr; |
376 |
0 |
JSAtom *atom; |
377 |
0 |
int stackDummy; |
378 |
|
|
379 |
0 |
if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { |
380 |
0 |
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); |
381 |
0 |
return JS_FALSE; |
382 |
|
} |
383 |
|
|
384 |
0 |
ok = js_GetLengthProperty(cx, obj, &length); |
385 |
0 |
if (!ok) |
386 |
0 |
return JS_FALSE; |
387 |
|
|
388 |
0 |
he = js_EnterSharpObject(cx, obj, NULL, &chars); |
389 |
0 |
if (!he) |
390 |
0 |
return JS_FALSE; |
391 |
0 |
if (literalize) { |
392 |
0 |
if (IS_SHARP(he)) { |
393 |
|
#if JS_HAS_SHARP_VARS |
394 |
0 |
nchars = js_strlen(chars); |
395 |
|
#else |
396 |
|
chars[0] = '['; |
397 |
|
chars[1] = ']'; |
398 |
|
chars[2] = 0; |
399 |
|
nchars = 2; |
400 |
|
#endif |
401 |
0 |
goto make_string; |
402 |
|
} |
403 |
|
|
404 |
|
/* |
405 |
|
* Allocate 1 + 3 + 1 for "[", the worst-case closing ", ]", and the |
406 |
|
* terminating 0. |
407 |
|
*/ |
408 |
0 |
growth = (1 + 3 + 1) * sizeof(jschar); |
409 |
0 |
if (!chars) { |
410 |
0 |
nchars = 0; |
411 |
0 |
chars = (jschar *) malloc(growth); |
412 |
0 |
if (!chars) |
413 |
0 |
goto done; |
414 |
|
} else { |
415 |
0 |
MAKE_SHARP(he); |
416 |
0 |
nchars = js_strlen(chars); |
417 |
0 |
chars = (jschar *) |
418 |
|
realloc((ochars = chars), nchars * sizeof(jschar) + growth); |
419 |
0 |
if (!chars) { |
420 |
0 |
free(ochars); |
421 |
0 |
goto done; |
422 |
|
} |
423 |
|
} |
424 |
0 |
chars[nchars++] = '['; |
425 |
|
} else { |
426 |
|
/* |
427 |
|
* Free any sharp variable definition in chars. Normally, we would |
428 |
|
* MAKE_SHARP(he) so that only the first sharp variable annotation is |
429 |
|
* a definition, and all the rest are references, but in the current |
430 |
|
* case of (!literalize), we don't need chars at all. |
431 |
|
*/ |
432 |
0 |
if (chars) |
433 |
0 |
JS_free(cx, chars); |
434 |
0 |
chars = NULL; |
435 |
0 |
nchars = 0; |
436 |
|
|
437 |
|
/* Return the empty string on a cycle as well as on empty join. */ |
438 |
0 |
if (IS_BUSY(he) || length == 0) { |
439 |
0 |
js_LeaveSharpObject(cx, NULL); |
440 |
0 |
*rval = JS_GetEmptyStringValue(cx); |
441 |
0 |
return ok; |
442 |
|
} |
443 |
|
|
444 |
|
/* Flag he as BUSY so we can distinguish a cycle from a join-point. */ |
445 |
0 |
MAKE_BUSY(he); |
446 |
|
} |
447 |
0 |
sepstr = NULL; |
448 |
0 |
seplen = JSSTRING_LENGTH(sep); |
449 |
|
|
450 |
|
/* Use rval to locally root each element value as we loop and convert. */ |
451 |
|
#define v (*rval) |
452 |
|
|
453 |
0 |
v = JSVAL_NULL; |
454 |
0 |
for (index = 0; index < length; index++) { |
455 |
0 |
ok = JS_GetElement(cx, obj, index, &v); |
456 |
0 |
if (!ok) |
457 |
0 |
goto done; |
458 |
|
|
459 |
0 |
if ((!literalize || JS_VERSION_IS_1_2(cx)) && |
460 |
|
(JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v))) { |
461 |
0 |
str = cx->runtime->emptyString; |
462 |
|
} else { |
463 |
0 |
if (localeString) { |
464 |
0 |
atom = cx->runtime->atomState.toLocaleStringAtom; |
465 |
0 |
JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr); |
466 |
0 |
ok = js_ValueToObject(cx, v, &tvr.u.object) && |
467 |
|
js_TryMethod(cx, tvr.u.object, atom, 0, NULL, &v); |
468 |
0 |
JS_POP_TEMP_ROOT(cx, &tvr); |
469 |
0 |
if (!ok) |
470 |
0 |
goto done; |
471 |
0 |
str = js_ValueToString(cx, v); |
472 |
|
} else { |
473 |
0 |
str = (literalize ? js_ValueToSource : js_ValueToString)(cx, v); |
474 |
|
} |
475 |
0 |
if (!str) { |
476 |
0 |
ok = JS_FALSE; |
477 |
0 |
goto done; |
478 |
|
} |
479 |
|
} |
480 |
|
|
481 |
|
/* Allocate 3 + 1 at end for ", ", closing bracket, and zero. */ |
482 |
0 |
tmplen = JSSTRING_LENGTH(str); |
483 |
0 |
growth = (nchars + (sepstr ? seplen : 0) + tmplen + 3 + 1); |
484 |
0 |
if (nchars > growth || tmplen > growth || |
485 |
|
growth > (size_t)-1 / sizeof(jschar)) { |
486 |
0 |
if (chars) { |
487 |
0 |
free(chars); |
488 |
0 |
chars = NULL; |
489 |
|
} |
490 |
0 |
JS_ReportOutOfMemory(cx); |
491 |
0 |
goto done; |
492 |
|
} |
493 |
0 |
growth *= sizeof(jschar); |
494 |
0 |
if (!chars) { |
495 |
0 |
chars = (jschar *) malloc(growth); |
496 |
0 |
if (!chars) |
497 |
0 |
goto done; |
498 |
|
} else { |
499 |
0 |
chars = (jschar *) realloc((ochars = chars), growth); |
500 |
0 |
if (!chars) { |
501 |
0 |
free(ochars); |
502 |
0 |
goto done; |
503 |
|
} |
504 |
|
} |
505 |
|
|
506 |
0 |
if (sepstr) { |
507 |
0 |
js_strncpy(&chars[nchars], sepstr, seplen); |
508 |
0 |
nchars += seplen; |
509 |
|
} |
510 |
0 |
sepstr = JSSTRING_CHARS(sep); |
511 |
|
|
512 |
0 |
js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen); |
513 |
0 |
nchars += tmplen; |
514 |
|
} |
515 |
|
|
516 |
|
done: |
517 |
0 |
if (literalize) { |
518 |
0 |
if (chars) { |
519 |
0 |
if (JSVAL_IS_VOID(v)) { |
520 |
0 |
chars[nchars++] = ','; |
521 |
0 |
chars[nchars++] = ' '; |
522 |
|
} |
523 |
0 |
chars[nchars++] = ']'; |
524 |
|
} |
525 |
|
} else { |
526 |
0 |
CLEAR_BUSY(he); |
527 |
|
} |
528 |
0 |
js_LeaveSharpObject(cx, NULL); |
529 |
0 |
if (!ok) { |
530 |
0 |
if (chars) |
531 |
0 |
free(chars); |
532 |
0 |
return ok; |
533 |
|
} |
534 |
|
|
535 |
|
#undef v |
536 |
|
|
537 |
|
make_string: |
538 |
0 |
if (!chars) { |
539 |
0 |
JS_ReportOutOfMemory(cx); |
540 |
0 |
return JS_FALSE; |
541 |
|
} |
542 |
0 |
chars[nchars] = 0; |
543 |
0 |
str = js_NewString(cx, chars, nchars, 0); |
544 |
0 |
if (!str) { |
545 |
0 |
free(chars); |
546 |
0 |
return JS_FALSE; |
547 |
|
} |
548 |
0 |
*rval = STRING_TO_JSVAL(str); |
549 |
0 |
return JS_TRUE; |
550 |
|
} |
551 |
|
|
552 |
|
static jschar comma_space_ucstr[] = {',', ' ', 0}; |
553 |
|
static jschar comma_ucstr[] = {',', 0}; |
554 |
|
static JSString comma_space = {2, comma_space_ucstr}; |
555 |
|
static JSString comma = {1, comma_ucstr}; |
556 |
|
|
557 |
|
#if JS_HAS_TOSOURCE |
558 |
|
static JSBool |
559 |
|
array_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
560 |
|
jsval *rval) |
561 |
0 |
{ |
562 |
0 |
return array_join_sub(cx, obj, &comma_space, JS_TRUE, rval, JS_FALSE); |
563 |
|
} |
564 |
|
#endif |
565 |
|
|
566 |
|
static JSBool |
567 |
|
array_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
568 |
|
jsval *rval) |
569 |
0 |
{ |
570 |
0 |
JSBool literalize; |
571 |
|
|
572 |
|
/* |
573 |
|
* JS1.2 arrays convert to array literals, with a comma followed by a space |
574 |
|
* between each element. |
575 |
|
*/ |
576 |
0 |
literalize = JS_VERSION_IS_1_2(cx); |
577 |
0 |
return array_join_sub(cx, obj, literalize ? &comma_space : &comma, |
578 |
|
literalize, rval, JS_FALSE); |
579 |
|
} |
580 |
|
|
581 |
|
static JSBool |
582 |
|
array_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
583 |
|
jsval *rval) |
584 |
0 |
{ |
585 |
|
/* |
586 |
|
* Passing comma here as the separator. Need a way to get a |
587 |
|
* locale-specific version. |
588 |
|
*/ |
589 |
0 |
return array_join_sub(cx, obj, &comma, JS_FALSE, rval, JS_TRUE); |
590 |
|
} |
591 |
|
|
592 |
|
static JSBool |
593 |
|
InitArrayElements(JSContext *cx, JSObject *obj, jsuint length, jsval *vector) |
594 |
0 |
{ |
595 |
0 |
jsuint index; |
596 |
0 |
jsid id; |
597 |
|
|
598 |
0 |
for (index = 0; index < length; index++) { |
599 |
0 |
JS_ASSERT(vector[index] != JSVAL_HOLE); |
600 |
|
|
601 |
0 |
if (!IndexToId(cx, index, &id)) |
602 |
0 |
return JS_FALSE; |
603 |
0 |
if (!OBJ_SET_PROPERTY(cx, obj, id, &vector[index])) |
604 |
0 |
return JS_FALSE; |
605 |
|
} |
606 |
0 |
return JS_TRUE; |
607 |
|
} |
608 |
|
|
609 |
|
static JSBool |
610 |
|
InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector) |
611 |
45620 |
{ |
612 |
45620 |
jsval v; |
613 |
45620 |
jsid id; |
614 |
|
|
615 |
45620 |
if (!IndexToValue(cx, length, &v)) |
616 |
0 |
return JS_FALSE; |
617 |
45620 |
id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); |
618 |
45620 |
if (!OBJ_DEFINE_PROPERTY(cx, obj, id, v, |
619 |
|
array_length_getter, array_length_setter, |
620 |
|
JSPROP_PERMANENT, |
621 |
|
NULL)) { |
622 |
0 |
return JS_FALSE; |
623 |
|
} |
624 |
45620 |
if (!vector) |
625 |
45620 |
return JS_TRUE; |
626 |
0 |
return InitArrayElements(cx, obj, length, vector); |
627 |
|
} |
628 |
|
|
629 |
|
#if JS_HAS_SOME_PERL_FUN |
630 |
|
/* |
631 |
|
* Perl-inspired join, reverse, and sort. |
632 |
|
*/ |
633 |
|
static JSBool |
634 |
|
array_join(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
635 |
0 |
{ |
636 |
0 |
JSString *str; |
637 |
|
|
638 |
0 |
if (JSVAL_IS_VOID(argv[0])) |
639 |
0 |
return array_join_sub(cx, obj, &comma, JS_FALSE, rval, JS_FALSE); |
640 |
0 |
str = js_ValueToString(cx, argv[0]); |
641 |
0 |
if (!str) |
642 |
0 |
return JS_FALSE; |
643 |
0 |
argv[0] = STRING_TO_JSVAL(str); |
644 |
0 |
return array_join_sub(cx, obj, str, JS_FALSE, rval, JS_FALSE); |
645 |
|
} |
646 |
|
|
647 |
|
static JSBool |
648 |
|
array_reverse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
649 |
|
jsval *rval) |
650 |
1127 |
{ |
651 |
1127 |
jsuint len, half, i; |
652 |
1127 |
jsid id, id2; |
653 |
1127 |
jsval *tmproot, *tmproot2; |
654 |
1127 |
JSBool idexists, id2exists, ok; |
655 |
|
|
656 |
1127 |
if (!js_GetLengthProperty(cx, obj, &len)) |
657 |
0 |
return JS_FALSE; |
658 |
|
|
659 |
|
/* |
660 |
|
* When len > JSVAL_INT_MAX + 1 the loop below accesses indexes greater |
661 |
|
* than JSVAL_INT_MAX. For such indexes the corresponding ids are atoms. |
662 |
|
* We use JS_KEEP_ATOMS to protect them against GC since OBJ_GET_PROPERTY |
663 |
|
* can potentially execute an arbitrary script. See bug 341956. |
664 |
|
* |
665 |
|
* After this point control must flow through label out: to exit. |
666 |
|
*/ |
667 |
1127 |
if (len > JSVAL_INT_MAX + 1) |
668 |
0 |
JS_KEEP_ATOMS(cx->runtime); |
669 |
|
|
670 |
|
/* |
671 |
|
* Use argv[argc] and argv[argc + 1] as local roots to hold temporarily |
672 |
|
* array elements for GC-safe swap. |
673 |
|
*/ |
674 |
1127 |
tmproot = argv + argc; |
675 |
1127 |
tmproot2 = argv + argc + 1; |
676 |
1127 |
half = len / 2; |
677 |
1399 |
for (i = 0; i < half; i++) { |
678 |
|
/* |
679 |
|
* Get both values while checking for holes to make sure they don't |
680 |
|
* get filled. |
681 |
|
*/ |
682 |
272 |
if (!IndexToId(cx, i, &id)) |
683 |
0 |
goto bad; |
684 |
272 |
if (!PropertyExists(cx, obj, id, &idexists)) |
685 |
0 |
goto bad; |
686 |
272 |
if (idexists && !OBJ_GET_PROPERTY(cx, obj, id, tmproot)) |
687 |
0 |
goto bad; |
688 |
|
|
689 |
272 |
if (!IndexToId(cx, len - i - 1, &id2)) |
690 |
0 |
goto bad; |
691 |
272 |
if (!PropertyExists(cx, obj, id2, &id2exists)) |
692 |
0 |
goto bad; |
693 |
272 |
if (id2exists && !OBJ_GET_PROPERTY(cx, obj, id2, tmproot2)) |
694 |
0 |
goto bad; |
695 |
|
|
696 |
|
/* Exchange the values. */ |
697 |
272 |
if (idexists) { |
698 |
272 |
if (!OBJ_SET_PROPERTY(cx, obj, id2, tmproot)) |
699 |
0 |
goto bad; |
700 |
|
} else { |
701 |
0 |
if (!OBJ_DELETE_PROPERTY(cx, obj, id2, tmproot)) |
702 |
0 |
goto bad; |
703 |
|
} |
704 |
272 |
if (id2exists) { |
705 |
272 |
if (!OBJ_SET_PROPERTY(cx, obj, id, tmproot2)) |
706 |
0 |
goto bad; |
707 |
|
} else { |
708 |
0 |
if (!OBJ_DELETE_PROPERTY(cx, obj, id, tmproot2)) |
709 |
0 |
goto bad; |
710 |
|
} |
711 |
|
} |
712 |
1127 |
ok = JS_TRUE; |
713 |
|
|
714 |
|
out: |
715 |
1127 |
if (len > JSVAL_INT_MAX + 1) |
716 |
0 |
JS_UNKEEP_ATOMS(cx->runtime); |
717 |
1127 |
*rval = OBJECT_TO_JSVAL(obj); |
718 |
1127 |
return ok; |
719 |
|
|
720 |
|
bad: |
721 |
0 |
ok = JS_FALSE; |
722 |
0 |
goto out; |
723 |
|
} |
724 |
|
|
725 |
|
typedef struct HSortArgs { |
726 |
|
void *vec; |
727 |
|
size_t elsize; |
728 |
|
void *pivot; |
729 |
|
JSComparator cmp; |
730 |
|
void *arg; |
731 |
|
JSBool fastcopy; |
732 |
|
} HSortArgs; |
733 |
|
|
734 |
|
static int |
735 |
|
sort_compare(const void *a, const void *b, void *arg); |
736 |
|
|
737 |
|
static int |
738 |
|
sort_compare_strings(const void *a, const void *b, void *arg); |
739 |
|
|
740 |
|
static void |
741 |
|
HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) |
742 |
0 |
{ |
743 |
0 |
void *pivot, *vec, *vec2, *arg, *a, *b; |
744 |
0 |
size_t elsize; |
745 |
0 |
JSComparator cmp; |
746 |
0 |
JSBool fastcopy; |
747 |
0 |
size_t j, hiDiv2; |
748 |
|
|
749 |
0 |
pivot = hsa->pivot; |
750 |
0 |
vec = hsa->vec; |
751 |
0 |
elsize = hsa->elsize; |
752 |
0 |
vec2 = (char *)vec - 2 * elsize; |
753 |
0 |
cmp = hsa->cmp; |
754 |
0 |
arg = hsa->arg; |
755 |
|
|
756 |
0 |
fastcopy = hsa->fastcopy; |
757 |
|
#define MEMCPY(p,q,n) \ |
758 |
|
(fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n)) |
759 |
|
|
760 |
0 |
if (lo == 1) { |
761 |
0 |
j = 2; |
762 |
0 |
b = (char *)vec + elsize; |
763 |
0 |
if (j < hi && cmp(vec, b, arg) < 0) |
764 |
0 |
j++; |
765 |
0 |
a = (char *)vec + (hi - 1) * elsize; |
766 |
0 |
b = (char *)vec2 + j * elsize; |
767 |
|
|
768 |
|
/* |
769 |
|
* During sorting phase b points to a member of heap that cannot be |
770 |
|
* bigger then biggest of vec[0] and vec[1], and cmp(a, b, arg) <= 0 |
771 |
|
* always holds. |
772 |
|
*/ |
773 |
0 |
if ((building || hi == 2) && cmp(a, b, arg) >= 0) |
774 |
0 |
return; |
775 |
|
|
776 |
0 |
MEMCPY(pivot, a, elsize); |
777 |
0 |
MEMCPY(a, b, elsize); |
778 |
0 |
lo = j; |
779 |
|
} else { |
780 |
0 |
a = (char *)vec2 + lo * elsize; |
781 |
0 |
MEMCPY(pivot, a, elsize); |
782 |
|
} |
783 |
|
|
784 |
0 |
hiDiv2 = hi/2; |
785 |
0 |
while (lo <= hiDiv2) { |
786 |
0 |
j = lo + lo; |
787 |
0 |
a = (char *)vec2 + j * elsize; |
788 |
0 |
b = (char *)vec + (j - 1) * elsize; |
789 |
0 |
if (j < hi && cmp(a, b, arg) < 0) |
790 |
0 |
j++; |
791 |
0 |
b = (char *)vec2 + j * elsize; |
792 |
0 |
if (cmp(pivot, b, arg) >= 0) |
793 |
0 |
break; |
794 |
|
|
795 |
0 |
a = (char *)vec2 + lo * elsize; |
796 |
0 |
MEMCPY(a, b, elsize); |
797 |
0 |
lo = j; |
798 |
|
} |
799 |
|
|
800 |
0 |
a = (char *)vec2 + lo * elsize; |
801 |
0 |
MEMCPY(a, pivot, elsize); |
802 |
|
#undef MEMCPY |
803 |
|
} |
804 |
|
|
805 |
|
void |
806 |
|
js_HeapSort(void *vec, size_t nel, void *pivot, size_t elsize, |
807 |
|
JSComparator cmp, void *arg) |
808 |
0 |
{ |
809 |
0 |
HSortArgs hsa; |
810 |
0 |
size_t i; |
811 |
|
|
812 |
0 |
hsa.vec = vec; |
813 |
0 |
hsa.elsize = elsize; |
814 |
0 |
hsa.pivot = pivot; |
815 |
0 |
hsa.cmp = cmp; |
816 |
0 |
hsa.arg = arg; |
817 |
0 |
hsa.fastcopy = (cmp == sort_compare || cmp == sort_compare_strings); |
818 |
|
|
819 |
0 |
for (i = nel/2; i != 0; i--) |
820 |
0 |
HeapSortHelper(JS_TRUE, &hsa, i, nel); |
821 |
0 |
while (nel > 2) |
822 |
0 |
HeapSortHelper(JS_FALSE, &hsa, 1, --nel); |
823 |
|
} |
824 |
|
|
825 |
|
typedef struct CompareArgs { |
826 |
|
JSContext *context; |
827 |
|
jsval fval; |
828 |
|
jsval *localroot; /* need one local root, for sort_compare */ |
829 |
|
JSBool status; |
830 |
|
} CompareArgs; |
831 |
|
|
832 |
|
static int |
833 |
|
sort_compare(const void *a, const void *b, void *arg) |
834 |
0 |
{ |
835 |
0 |
jsval av = *(const jsval *)a, bv = *(const jsval *)b; |
836 |
0 |
CompareArgs *ca = (CompareArgs *) arg; |
837 |
0 |
JSContext *cx = ca->context; |
838 |
0 |
jsdouble cmp = -1; |
839 |
0 |
jsval fval, argv[2], special; |
840 |
0 |
JSBool ok; |
841 |
|
|
842 |
0 |
fval = ca->fval; |
843 |
|
|
844 |
|
/* |
845 |
|
* By ECMA 262, 15.4.4.11, existence of the property with value undefined |
846 |
|
* takes precedence over an undefined property (which we call a "hole"). |
847 |
|
*/ |
848 |
0 |
if (av == JSVAL_HOLE || bv == JSVAL_HOLE) |
849 |
0 |
special = JSVAL_HOLE; |
850 |
0 |
else if (av == JSVAL_VOID || bv == JSVAL_VOID) |
851 |
0 |
special = JSVAL_VOID; |
852 |
|
else |
853 |
0 |
special = JSVAL_NULL; |
854 |
|
|
855 |
0 |
if (special != JSVAL_NULL) { |
856 |
0 |
if (av == bv) |
857 |
0 |
cmp = 0; |
858 |
0 |
else if (av != special) |
859 |
0 |
cmp = -1; |
860 |
|
else |
861 |
0 |
cmp = 1; |
862 |
0 |
} else if (fval == JSVAL_NULL) { |
863 |
0 |
JSString *astr, *bstr; |
864 |
|
|
865 |
0 |
if (av == bv) { |
866 |
0 |
cmp = 0; |
867 |
|
} else { |
868 |
|
/* |
869 |
|
* Set our local root to astr in case the second js_ValueToString |
870 |
|
* displaces the newborn root in cx, and the GC nests under that |
871 |
|
* call. Don't bother guarding the local root store with an astr |
872 |
|
* non-null test. If we tag null as a string, the GC will untag, |
873 |
|
* null-test, and avoid dereferencing null. |
874 |
|
*/ |
875 |
0 |
astr = js_ValueToString(cx, av); |
876 |
0 |
*ca->localroot = STRING_TO_JSVAL(astr); |
877 |
0 |
if (astr && (bstr = js_ValueToString(cx, bv))) |
878 |
0 |
cmp = js_CompareStrings(astr, bstr); |
879 |
|
else |
880 |
0 |
ca->status = JS_FALSE; |
881 |
|
} |
882 |
|
} else { |
883 |
0 |
argv[0] = av; |
884 |
0 |
argv[1] = bv; |
885 |
0 |
ok = js_InternalCall(cx, |
886 |
|
OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fval)), |
887 |
|
fval, 2, argv, ca->localroot); |
888 |
0 |
if (ok) { |
889 |
0 |
ok = js_ValueToNumber(cx, *ca->localroot, &cmp); |
890 |
|
|
891 |
|
/* Clamp cmp to -1, 0, 1. */ |
892 |
0 |
if (ok) { |
893 |
0 |
if (JSDOUBLE_IS_NaN(cmp)) { |
894 |
|
/* |
895 |
|
* XXX report some kind of error here? ECMA talks about |
896 |
|
* 'consistent compare functions' that don't return NaN, |
897 |
|
* but is silent about what the result should be. So we |
898 |
|
* currently ignore it. |
899 |
|
*/ |
900 |
0 |
cmp = 0; |
901 |
0 |
} else if (cmp != 0) { |
902 |
0 |
cmp = cmp > 0 ? 1 : -1; |
903 |
|
} |
904 |
|
} |
905 |
|
} |
906 |
0 |
if (!ok) |
907 |
0 |
ca->status = ok; |
908 |
|
} |
909 |
0 |
return (int)cmp; |
910 |
|
} |
911 |
|
|
912 |
|
static int |
913 |
|
sort_compare_strings(const void *a, const void *b, void *arg) |
914 |
0 |
{ |
915 |
0 |
jsval av = *(const jsval *)a, bv = *(const jsval *)b; |
916 |
|
|
917 |
0 |
return (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv)); |
918 |
|
} |
919 |
|
|
920 |
|
static JSBool |
921 |
|
array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
922 |
0 |
{ |
923 |
0 |
jsval fval, *vec, *pivotroot; |
924 |
0 |
CompareArgs ca; |
925 |
0 |
jsuint len, newlen, i; |
926 |
0 |
JSStackFrame *fp; |
927 |
0 |
jsid id; |
928 |
0 |
size_t nbytes; |
929 |
|
|
930 |
|
/* |
931 |
|
* Optimize the default compare function case if all of obj's elements |
932 |
|
* have values of type string. |
933 |
|
*/ |
934 |
0 |
JSBool all_strings; |
935 |
|
|
936 |
0 |
if (argc > 0) { |
937 |
0 |
if (JSVAL_IS_PRIMITIVE(argv[0])) { |
938 |
0 |
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
939 |
|
JSMSG_BAD_SORT_ARG); |
940 |
0 |
return JS_FALSE; |
941 |
|
} |
942 |
0 |
fval = argv[0]; |
943 |
0 |
all_strings = JS_FALSE; /* non-default compare function */ |
944 |
|
} else { |
945 |
0 |
fval = JSVAL_NULL; |
946 |
0 |
all_strings = JS_TRUE; /* check for all string values */ |
947 |
|
} |
948 |
|
|
949 |
0 |
if (!js_GetLengthProperty(cx, obj, &len)) |
950 |
0 |
return JS_FALSE; |
951 |
0 |
if (len == 0) { |
952 |
0 |
*rval = OBJECT_TO_JSVAL(obj); |
953 |
0 |
return JS_TRUE; |
954 |
|
} |
955 |
|
|
956 |
|
/* |
957 |
|
* We need a temporary array of len jsvals to hold elements of the array. |
958 |
|
* Check that its size does not overflow size_t, which would allow for |
959 |
|
* indexing beyond the end of the malloc'd vector. |
960 |
|
*/ |
961 |
0 |
if (len > ((size_t) -1) / sizeof(jsval)) { |
962 |
0 |
JS_ReportOutOfMemory(cx); |
963 |
0 |
return JS_FALSE; |
964 |
|
} |
965 |
0 |
nbytes = ((size_t) len) * sizeof(jsval); |
966 |
|
|
967 |
0 |
vec = (jsval *) JS_malloc(cx, nbytes); |
968 |
0 |
if (!vec) |
969 |
0 |
return JS_FALSE; |
970 |
|
|
971 |
|
/* Root vec, clearing it first in case a GC nests while we're filling it. */ |
972 |
0 |
memset(vec, 0, nbytes); |
973 |
0 |
fp = cx->fp; |
974 |
0 |
fp->vars = vec; |
975 |
0 |
fp->nvars = len; |
976 |
|
|
977 |
0 |
newlen = 0; |
978 |
0 |
for (i = 0; i < len; i++) { |
979 |
0 |
ca.status = IndexToExistingId(cx, obj, i, &id); |
980 |
0 |
if (!ca.status) |
981 |
0 |
goto out; |
982 |
|
|
983 |
0 |
if (id == JSID_HOLE) { |
984 |
0 |
vec[i] = JSVAL_HOLE; |
985 |
0 |
all_strings = JS_FALSE; |
986 |
0 |
continue; |
987 |
|
} |
988 |
0 |
newlen++; |
989 |
|
|
990 |
0 |
ca.status = OBJ_GET_PROPERTY(cx, obj, id, &vec[i]); |
991 |
0 |
if (!ca.status) |
992 |
0 |
goto out; |
993 |
|
|
994 |
|
/* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */ |
995 |
0 |
all_strings &= JSVAL_IS_STRING(vec[i]); |
996 |
|
} |
997 |
|
|
998 |
0 |
ca.context = cx; |
999 |
0 |
ca.fval = fval; |
1000 |
0 |
ca.localroot = argv + argc; /* local GC root for temporary string */ |
1001 |
0 |
pivotroot = argv + argc + 1; /* local GC root for pivot val */ |
1002 |
0 |
ca.status = JS_TRUE; |
1003 |
0 |
js_HeapSort(vec, (size_t) len, pivotroot, sizeof(jsval), |
1004 |
|
all_strings ? sort_compare_strings : sort_compare, |
1005 |
|
&ca); |
1006 |
|
|
1007 |
0 |
if (ca.status) { |
1008 |
0 |
ca.status = InitArrayElements(cx, obj, newlen, vec); |
1009 |
0 |
if (ca.status) |
1010 |
0 |
*rval = OBJECT_TO_JSVAL(obj); |
1011 |
|
|
1012 |
|
/* Re-create any holes that sorted to the end of the array. */ |
1013 |
0 |
while (len > newlen) { |
1014 |
0 |
jsval junk; |
1015 |
|
|
1016 |
0 |
if (!IndexToId(cx, --len, &id)) |
1017 |
0 |
return JS_FALSE; |
1018 |
0 |
if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk)) |
1019 |
0 |
return JS_FALSE; |
1020 |
|
} |
1021 |
|
} |
1022 |
|
|
1023 |
|
out: |
1024 |
0 |
if (vec) |
1025 |
0 |
JS_free(cx, vec); |
1026 |
0 |
return ca.status; |
1027 |
|
} |
1028 |
|
#endif /* JS_HAS_SOME_PERL_FUN */ |
1029 |
|
|
1030 |
|
#if JS_HAS_MORE_PERL_FUN |
1031 |
|
/* |
1032 |
|
* Perl-inspired push, pop, shift, unshift, and splice methods. |
1033 |
|
*/ |
1034 |
|
static JSBool |
1035 |
|
array_push(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
1036 |
3972 |
{ |
1037 |
3972 |
jsuint length; |
1038 |
3972 |
uintN i; |
1039 |
3972 |
jsid id; |
1040 |
|
|
1041 |
3972 |
if (!js_GetLengthProperty(cx, obj, &length)) |
1042 |
0 |
return JS_FALSE; |
1043 |
7944 |
for (i = 0; i < argc; i++) { |
1044 |
3972 |
if (!IndexToId(cx, length + i, &id)) |
1045 |
0 |
return JS_FALSE; |
1046 |
3972 |
if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i])) |
1047 |
0 |
return JS_FALSE; |
1048 |
|
} |
1049 |
|
|
1050 |
|
/* |
1051 |
|
* If JS1.2, follow Perl4 by returning the last thing pushed. Otherwise, |
1052 |
|
* return the new array length. |
1053 |
|
*/ |
1054 |
3972 |
length += argc; |
1055 |
3972 |
if (JS_VERSION_IS_1_2(cx)) { |
1056 |
0 |
*rval = argc ? argv[argc-1] : JSVAL_VOID; |
1057 |
|
} else { |
1058 |
3972 |
if (!IndexToValue(cx, length, rval)) |
1059 |
0 |
return JS_FALSE; |
1060 |
|
} |
1061 |
3972 |
return js_SetLengthProperty(cx, obj, length); |
1062 |
|
} |
1063 |
|
|
1064 |
|
static JSBool |
1065 |
|
array_pop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
1066 |
0 |
{ |
1067 |
0 |
jsuint index; |
1068 |
0 |
jsid id; |
1069 |
0 |
JSBool ok; |
1070 |
0 |
jsval junk; |
1071 |
|
|
1072 |
0 |
if (!js_GetLengthProperty(cx, obj, &index)) |
1073 |
0 |
return JS_FALSE; |
1074 |
0 |
if (index > 0) { |
1075 |
0 |
index--; |
1076 |
|
|
1077 |
|
/* Get the to-be-deleted property's value into rval. */ |
1078 |
0 |
if (!IndexToId(cx, index, &id)) |
1079 |
0 |
return JS_FALSE; |
1080 |
|
|
1081 |
|
/* See comments in array_reverse. */ |
1082 |
0 |
if (index > JSVAL_INT_MAX) |
1083 |
0 |
JS_KEEP_ATOMS(cx->runtime); |
1084 |
0 |
ok = OBJ_GET_PROPERTY(cx, obj, id, rval) && |
1085 |
|
OBJ_DELETE_PROPERTY(cx, obj, id, &junk); |
1086 |
0 |
if (index > JSVAL_INT_MAX) |
1087 |
0 |
JS_UNKEEP_ATOMS(cx->runtime); |
1088 |
0 |
if (!ok) |
1089 |
0 |
return JS_FALSE; |
1090 |
|
} |
1091 |
0 |
return js_SetLengthProperty(cx, obj, index); |
1092 |
|
} |
1093 |
|
|
1094 |
|
static JSBool |
1095 |
|
array_shift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
1096 |
0 |
{ |
1097 |
0 |
jsuint length, i; |
1098 |
0 |
jsid id, id2; |
1099 |
0 |
jsval junk; |
1100 |
|
|
1101 |
0 |
if (!js_GetLengthProperty(cx, obj, &length)) |
1102 |
0 |
return JS_FALSE; |
1103 |
0 |
if (length > 0) { |
1104 |
0 |
length--; |
1105 |
0 |
id = JSVAL_ZERO; |
1106 |
|
|
1107 |
|
/* Get the to-be-deleted property's value into rval ASAP. */ |
1108 |
0 |
if (!OBJ_GET_PROPERTY(cx, obj, id, rval)) |
1109 |
0 |
return JS_FALSE; |
1110 |
|
|
1111 |
|
/* |
1112 |
|
* Slide down the array above the first element. |
1113 |
|
*/ |
1114 |
0 |
if (length > 0) { |
1115 |
0 |
for (i = 1; i <= length; i++) { |
1116 |
0 |
if (!IndexToId(cx, i, &id)) |
1117 |
0 |
return JS_FALSE; |
1118 |
0 |
if (!OBJ_GET_PROPERTY(cx, obj, id, &argv[0])) |
1119 |
0 |
return JS_FALSE; |
1120 |
|
|
1121 |
|
/* Get id after value to avoid nested GC hazards. */ |
1122 |
0 |
if (!IndexToId(cx, i - 1, &id2)) |
1123 |
0 |
return JS_FALSE; |
1124 |
0 |
if (!OBJ_SET_PROPERTY(cx, obj, id2, &argv[0])) |
1125 |
0 |
return JS_FALSE; |
1126 |
|
} |
1127 |
|
} |
1128 |
|
|
1129 |
|
/* |
1130 |
|
* Delete the only or the last element. We recreate id when it is an |
1131 |
|
* atom to protect against a nested GC during the last iteration. |
1132 |
|
*/ |
1133 |
0 |
if (length > JSVAL_INT_MAX && !IndexToId(cx, length, &id)) |
1134 |
0 |
return JS_FALSE; |
1135 |
0 |
if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk)) |
1136 |
0 |
return JS_FALSE; |
1137 |
|
} |
1138 |
0 |
return js_SetLengthProperty(cx, obj, length); |
1139 |
|
} |
1140 |
|
|
1141 |
|
static JSBool |
1142 |
|
array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
1143 |
|
jsval *rval) |
1144 |
4926 |
{ |
1145 |
4926 |
jsuint length, last; |
1146 |
4926 |
uintN i; |
1147 |
4926 |
jsid id, id2; |
1148 |
4926 |
jsval *vp, junk; |
1149 |
|
|
1150 |
4926 |
if (!js_GetLengthProperty(cx, obj, &length)) |
1151 |
0 |
return JS_FALSE; |
1152 |
4926 |
if (argc > 0) { |
1153 |
|
/* Slide up the array to make room for argc at the bottom. */ |
1154 |
4926 |
if (length > 0) { |
1155 |
0 |
last = length; |
1156 |
0 |
vp = argv + argc; |
1157 |
0 |
while (last--) { |
1158 |
0 |
if (!IndexToExistingId(cx, obj, last, &id)) |
1159 |
0 |
return JS_FALSE; |
1160 |
0 |
if (id != JSID_HOLE) { |
1161 |
0 |
if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) |
1162 |
0 |
return JS_FALSE; |
1163 |
|
} |
1164 |
|
|
1165 |
|
/* Get id after value to avoid nested GC hazards. */ |
1166 |
0 |
if (!IndexToId(cx, last + argc, &id2)) |
1167 |
0 |
return JS_FALSE; |
1168 |
0 |
if (id == JSID_HOLE) { |
1169 |
0 |
if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk)) |
1170 |
0 |
return JS_FALSE; |
1171 |
|
} else { |
1172 |
0 |
if (!OBJ_SET_PROPERTY(cx, obj, id2, vp)) |
1173 |
0 |
return JS_FALSE; |
1174 |
|
} |
1175 |
|
} |
1176 |
|
} |
1177 |
|
|
1178 |
|
/* Copy from argv to the bottom of the array. */ |
1179 |
9852 |
for (i = 0; i < argc; i++) { |
1180 |
4926 |
if (!IndexToId(cx, i, &id)) |
1181 |
0 |
return JS_FALSE; |
1182 |
4926 |
if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i])) |
1183 |
0 |
return JS_FALSE; |
1184 |
|
} |
1185 |
|
|
1186 |
|
/* Follow Perl by returning the new array length. */ |
1187 |
4926 |
length += argc; |
1188 |
4926 |
if (!js_SetLengthProperty(cx, obj, length)) |
1189 |
0 |
return JS_FALSE; |
1190 |
|
} |
1191 |
4926 |
return IndexToValue(cx, length, rval); |
1192 |
|
} |
1193 |
|
|
1194 |
|
static JSBool |
1195 |
|
array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
1196 |
1130 |
{ |
1197 |
1130 |
jsval *vp, junk; |
1198 |
1130 |
jsuint length, begin, end, count, delta, last; |
1199 |
1130 |
jsdouble d; |
1200 |
1130 |
jsid id, id2; |
1201 |
1130 |
JSObject *obj2; |
1202 |
1130 |
uintN i; |
1203 |
|
|
1204 |
|
/* |
1205 |
|
* Nothing to do if no args. Otherwise point vp at our one explicit local |
1206 |
|
* root and get length. |
1207 |
|
*/ |
1208 |
1130 |
if (argc == 0) |
1209 |
0 |
return JS_TRUE; |
1210 |
1130 |
vp = argv + argc; |
1211 |
1130 |
if (!js_GetLengthProperty(cx, obj, &length)) |
1212 |
0 |
return JS_FALSE; |
1213 |
|
|
1214 |
|
/* Convert the first argument into a starting index. */ |
1215 |
1130 |
if (!js_ValueToNumber(cx, *argv, &d)) |
1216 |
0 |
return JS_FALSE; |
1217 |
1130 |
d = js_DoubleToInteger(d); |
1218 |
1130 |
if (d < 0) { |
1219 |
0 |
d += length; |
1220 |
0 |
if (d < 0) |
1221 |
0 |
d = 0; |
1222 |
1130 |
} else if (d > length) { |
1223 |
0 |
d = length; |
1224 |
|
} |
1225 |
1130 |
begin = (jsuint)d; /* d has been clamped to uint32 */ |
1226 |
1130 |
argc--; |
1227 |
1130 |
argv++; |
1228 |
|
|
1229 |
|
/* Convert the second argument from a count into a fencepost index. */ |
1230 |
1130 |
delta = length - begin; |
1231 |
1130 |
if (argc == 0) { |
1232 |
0 |
count = delta; |
1233 |
0 |
end = length; |
1234 |
|
} else { |
1235 |
1130 |
if (!js_ValueToNumber(cx, *argv, &d)) |
1236 |
0 |
return JS_FALSE; |
1237 |
1130 |
d = js_DoubleToInteger(d); |
1238 |
1130 |
if (d < 0) |
1239 |
0 |
d = 0; |
1240 |
1130 |
else if (d > delta) |
1241 |
0 |
d = delta; |
1242 |
1130 |
count = (jsuint)d; |
1243 |
1130 |
end = begin + count; |
1244 |
1130 |
argc--; |
1245 |
1130 |
argv++; |
1246 |
|
} |
1247 |
|
|
1248 |
1130 |
if (count == 1 && JS_VERSION_IS_1_2(cx)) { |
1249 |
|
/* |
1250 |
|
* JS lacks "list context", whereby in Perl one turns the single |
1251 |
|
* scalar that's spliced out into an array just by assigning it to |
1252 |
|
* @single instead of $single, or by using it as Perl push's first |
1253 |
|
* argument, for instance. |
1254 |
|
* |
1255 |
|
* JS1.2 emulated Perl too closely and returned a non-Array for |
1256 |
|
* the single-splice-out case, requiring callers to test and wrap |
1257 |
|
* in [] if necessary. So JS1.3, default, and other versions all |
1258 |
|
* return an array of length 1 for uniformity. |
1259 |
|
*/ |
1260 |
0 |
if (!IndexToId(cx, begin, &id)) |
1261 |
0 |
return JS_FALSE; |
1262 |
0 |
if (!OBJ_GET_PROPERTY(cx, obj, id, rval)) |
1263 |
0 |
return JS_FALSE; |
1264 |
|
} else { |
1265 |
1130 |
if (!JS_VERSION_IS_1_2(cx) || count > 0) { |
1266 |
|
/* |
1267 |
|
* Create a new array value to return. Our ECMA v2 proposal specs |
1268 |
|
* that splice always returns an array value, even when given no |
1269 |
|
* arguments. We think this is best because it eliminates the need |
1270 |
|
* for callers to do an extra test to handle the empty splice case. |
1271 |
|
*/ |
1272 |
1130 |
obj2 = js_NewArrayObject(cx, 0, NULL); |
1273 |
1130 |
if (!obj2) |
1274 |
0 |
return JS_FALSE; |
1275 |
1130 |
*rval = OBJECT_TO_JSVAL(obj2); |
1276 |
|
|
1277 |
|
/* If there are elements to remove, put them into the return value. */ |
1278 |
1130 |
if (count > 0) { |
1279 |
0 |
for (last = begin; last < end; last++) { |
1280 |
0 |
if (!IndexToExistingId(cx, obj, last, &id)) |
1281 |
0 |
return JS_FALSE; |
1282 |
0 |
if (id == JSID_HOLE) |
1283 |
0 |
continue; /* don't fill holes in the new array */ |
1284 |
0 |
if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) |
1285 |
0 |
return JS_FALSE; |
1286 |
|
|
1287 |
|
/* Get id after value to avoid nested GC hazards. */ |
1288 |
0 |
if (!IndexToId(cx, last - begin, &id2)) |
1289 |
0 |
return JS_FALSE; |
1290 |
0 |
if (!OBJ_SET_PROPERTY(cx, obj2, id2, vp)) |
1291 |
0 |
return JS_FALSE; |
1292 |
|
} |
1293 |
|
|
1294 |
0 |
if (!js_SetLengthProperty(cx, obj2, end - begin)) |
1295 |
0 |
return JS_FALSE; |
1296 |
|
} |
1297 |
|
} |
1298 |
|
} |
1299 |
|
|
1300 |
|
/* Find the direction (up or down) to copy and make way for argv. */ |
1301 |
1130 |
if (argc > count) { |
1302 |
1130 |
delta = (jsuint)argc - count; |
1303 |
1130 |
last = length; |
1304 |
|
/* (uint) end could be 0, so can't use vanilla >= test */ |
1305 |
1616 |
while (last-- > end) { |
1306 |
486 |
if (!IndexToExistingId(cx, obj, last, &id)) |
1307 |
0 |
return JS_FALSE; |
1308 |
486 |
if (id != JSID_HOLE) { |
1309 |
486 |
if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) |
1310 |
0 |
return JS_FALSE; |
1311 |
|
} |
1312 |
|
|
1313 |
|
/* Get id after value to avoid nested GC hazards. */ |
1314 |
486 |
if (!IndexToId(cx, last + delta, &id2)) |
1315 |
0 |
return JS_FALSE; |
1316 |
486 |
if (id != JSID_HOLE) { |
1317 |
486 |
if (!OBJ_SET_PROPERTY(cx, obj, id2, vp)) |
1318 |
0 |
return JS_FALSE; |
1319 |
|
} else { |
1320 |
0 |
if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk)) |
1321 |
0 |
return JS_FALSE; |
1322 |
|
} |
1323 |
|
} |
1324 |
1130 |
length += delta; |
1325 |
0 |
} else if (argc < count) { |
1326 |
0 |
delta = count - (jsuint)argc; |
1327 |
0 |
for (last = end; last < length; last++) { |
1328 |
0 |
if (!IndexToExistingId(cx, obj, last, &id)) |
1329 |
0 |
return JS_FALSE; |
1330 |
0 |
if (id != JSID_HOLE) { |
1331 |
0 |
if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) |
1332 |
0 |
return JS_FALSE; |
1333 |
|
} |
1334 |
|
|
1335 |
|
/* Get id after value to avoid nested GC hazards. */ |
1336 |
0 |
if (!IndexToId(cx, last - delta, &id2)) |
1337 |
0 |
return JS_FALSE; |
1338 |
0 |
if (id != JSID_HOLE) { |
1339 |
0 |
if (!OBJ_SET_PROPERTY(cx, obj, id2, vp)) |
1340 |
0 |
return JS_FALSE; |
1341 |
|
} else { |
1342 |
0 |
if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk)) |
1343 |
0 |
return JS_FALSE; |
1344 |
|
} |
1345 |
|
} |
1346 |
0 |
length -= delta; |
1347 |
|
} |
1348 |
|
|
1349 |
|
/* Copy from argv into the hole to complete the splice. */ |
1350 |
2260 |
for (i = 0; i < argc; i++) { |
1351 |
1130 |
if (!IndexToId(cx, begin + i, &id)) |
1352 |
0 |
return JS_FALSE; |
1353 |
1130 |
if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i])) |
1354 |
0 |
return JS_FALSE; |
1355 |
|
} |
1356 |
|
|
1357 |
|
/* Update length in case we deleted elements from the end. */ |
1358 |
1130 |
return js_SetLengthProperty(cx, obj, length); |
1359 |
|
} |
1360 |
|
#endif /* JS_HAS_MORE_PERL_FUN */ |
1361 |
|
|
1362 |
|
#if JS_HAS_SEQUENCE_OPS |
1363 |
|
/* |
1364 |
|
* Python-esque sequence operations. |
1365 |
|
*/ |
1366 |
|
static JSBool |
1367 |
|
array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
1368 |
0 |
{ |
1369 |
0 |
jsval *vp, v; |
1370 |
0 |
JSObject *nobj, *aobj; |
1371 |
0 |
jsuint length, alength, slot; |
1372 |
0 |
uintN i; |
1373 |
0 |
jsid id, id2; |
1374 |
|
|
1375 |
|
/* Hoist the explicit local root address computation. */ |
1376 |
0 |
vp = argv + argc; |
1377 |
|
|
1378 |
|
/* Treat obj as the first argument; see ECMA 15.4.4.4. */ |
1379 |
0 |
--argv; |
1380 |
0 |
JS_ASSERT(obj == JSVAL_TO_OBJECT(argv[0])); |
1381 |
|
|
1382 |
|
/* Create a new Array object and store it in the rval local root. */ |
1383 |
0 |
nobj = js_NewArrayObject(cx, 0, NULL); |
1384 |
0 |
if (!nobj) |
1385 |
0 |
return JS_FALSE; |
1386 |
0 |
*rval = OBJECT_TO_JSVAL(nobj); |
1387 |
|
|
1388 |
|
/* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */ |
1389 |
0 |
length = 0; |
1390 |
0 |
for (i = 0; i <= argc; i++) { |
1391 |
0 |
v = argv[i]; |
1392 |
0 |
if (JSVAL_IS_OBJECT(v)) { |
1393 |
0 |
aobj = JSVAL_TO_OBJECT(v); |
1394 |
0 |
if (aobj && OBJ_GET_CLASS(cx, aobj) == &js_ArrayClass) { |
1395 |
0 |
if (!OBJ_GET_PROPERTY(cx, aobj, |
1396 |
|
ATOM_TO_JSID(cx->runtime->atomState |
1397 |
|
.lengthAtom), |
1398 |
|
vp)) { |
1399 |
0 |
return JS_FALSE; |
1400 |
|
} |
1401 |
0 |
if (!ValueIsLength(cx, *vp, &alength)) |
1402 |
0 |
return JS_FALSE; |
1403 |
0 |
for (slot = 0; slot < alength; slot++) { |
1404 |
0 |
if (!IndexToExistingId(cx, aobj, slot, &id)) |
1405 |
0 |
return JS_FALSE; |
1406 |
0 |
if (id == JSID_HOLE) { |
1407 |
|
/* |
1408 |
|
* Per ECMA 262, 15.4.4.4, step 9, ignore non-existent |
1409 |
|
* properties. |
1410 |
|
*/ |
1411 |
0 |
continue; |
1412 |
|
} |
1413 |
0 |
if (!OBJ_GET_PROPERTY(cx, aobj, id, vp)) |
1414 |
0 |
return JS_FALSE; |
1415 |
|
|
1416 |
|
/* Get id after value to avoid nested GC hazards. */ |
1417 |
0 |
if (!IndexToId(cx, length + slot, &id2)) |
1418 |
0 |
return JS_FALSE; |
1419 |
0 |
if (!OBJ_SET_PROPERTY(cx, nobj, id2, vp)) |
1420 |
0 |
return JS_FALSE; |
1421 |
|
} |
1422 |
0 |
length += alength; |
1423 |
0 |
continue; |
1424 |
|
} |
1425 |
|
} |
1426 |
|
|
1427 |
0 |
*vp = v; |
1428 |
0 |
if (!IndexToId(cx, length, &id)) |
1429 |
0 |
return JS_FALSE; |
1430 |
0 |
if (!OBJ_SET_PROPERTY(cx, nobj, id, vp)) |
1431 |
0 |
return JS_FALSE; |
1432 |
0 |
length++; |
1433 |
|
} |
1434 |
|
|
1435 |
0 |
return js_SetLengthProperty(cx, nobj, length); |
1436 |
|
} |
1437 |
|
|
1438 |
|
static JSBool |
1439 |
|
array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
1440 |
0 |
{ |
1441 |
0 |
jsval *vp; |
1442 |
0 |
JSObject *nobj; |
1443 |
0 |
jsuint length, begin, end, slot; |
1444 |
0 |
jsdouble d; |
1445 |
0 |
jsid id, id2; |
1446 |
|
|
1447 |
|
/* Hoist the explicit local root address computation. */ |
1448 |
0 |
vp = argv + argc; |
1449 |
|
|
1450 |
|
/* Create a new Array object and store it in the rval local root. */ |
1451 |
0 |
nobj = js_NewArrayObject(cx, 0, NULL); |
1452 |
0 |
if (!nobj) |
1453 |
0 |
return JS_FALSE; |
1454 |
0 |
*rval = OBJECT_TO_JSVAL(nobj); |
1455 |
|
|
1456 |
0 |
if (!js_GetLengthProperty(cx, obj, &length)) |
1457 |
0 |
return JS_FALSE; |
1458 |
0 |
begin = 0; |
1459 |
0 |
end = length; |
1460 |
|
|
1461 |
0 |
if (argc > 0) { |
1462 |
0 |
if (!js_ValueToNumber(cx, argv[0], &d)) |
1463 |
0 |
return JS_FALSE; |
1464 |
0 |
d = js_DoubleToInteger(d); |
1465 |
0 |
if (d < 0) { |
1466 |
0 |
d += length; |
1467 |
0 |
if (d < 0) |
1468 |
0 |
d = 0; |
1469 |
0 |
} else if (d > length) { |
1470 |
0 |
d = length; |
1471 |
|
} |
1472 |
0 |
begin = (jsuint)d; |
1473 |
|
|
1474 |
0 |
if (argc > 1) { |
1475 |
0 |
if (!js_ValueToNumber(cx, argv[1], &d)) |
1476 |
0 |
return JS_FALSE; |
1477 |
0 |
d = js_DoubleToInteger(d); |
1478 |
0 |
if (d < 0) { |
1479 |
0 |
d += length; |
1480 |
0 |
if (d < 0) |
1481 |
0 |
d = 0; |
1482 |
0 |
} else if (d > length) { |
1483 |
0 |
d = length; |
1484 |
|
} |
1485 |
0 |
end = (jsuint)d; |
1486 |
|
} |
1487 |
|
} |
1488 |
|
|
1489 |
0 |
if (begin > end) |
1490 |
0 |
begin = end; |
1491 |
|
|
1492 |
0 |
for (slot = begin; slot < end; slot++) { |
1493 |
0 |
if (!IndexToExistingId(cx, obj, slot, &id)) |
1494 |
0 |
return JS_FALSE; |
1495 |
0 |
if (id == JSID_HOLE) |
1496 |
0 |
continue; |
1497 |
0 |
if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) |
1498 |
0 |
return JS_FALSE; |
1499 |
|
|
1500 |
|
/* Get id after value to avoid nested GC hazards. */ |
1501 |
0 |
if (!IndexToId(cx, slot - begin, &id2)) |
1502 |
0 |
return JS_FALSE; |
1503 |
0 |
if (!OBJ_SET_PROPERTY(cx, nobj, id2, vp)) |
1504 |
0 |
return JS_FALSE; |
1505 |
|
} |
1506 |
0 |
return js_SetLengthProperty(cx, nobj, end - begin); |
1507 |
|
} |
1508 |
|
#endif /* JS_HAS_SEQUENCE_OPS */ |
1509 |
|
|
1510 |
|
#if JS_HAS_ARRAY_EXTRAS |
1511 |
|
|
1512 |
|
static JSBool |
1513 |
|
array_indexOfHelper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
1514 |
|
jsval *rval, JSBool isLast) |
1515 |
0 |
{ |
1516 |
0 |
jsuint length, i, stop; |
1517 |
0 |
jsint direction; |
1518 |
|
|
1519 |
0 |
if (!js_GetLengthProperty(cx, obj, &length)) |
1520 |
0 |
return JS_FALSE; |
1521 |
0 |
if (length == 0) |
1522 |
0 |
goto not_found; |
1523 |
|
|
1524 |
0 |
if (argc <= 1) { |
1525 |
0 |
i = isLast ? length - 1 : 0; |
1526 |
|
} else { |
1527 |
0 |
jsdouble start; |
1528 |
|
|
1529 |
0 |
if (!js_ValueToNumber(cx, argv[1], &start)) |
1530 |
0 |
return JS_FALSE; |
1531 |
0 |
start = js_DoubleToInteger(start); |
1532 |
0 |
if (start < 0) { |
1533 |
0 |
start += length; |
1534 |
0 |
i = (start < 0) ? 0 : (jsuint)start; |
1535 |
0 |
} else if (start >= length) { |
1536 |
0 |
i = length - 1; |
1537 |
|
} else { |
1538 |
0 |
i = (jsuint)start; |
1539 |
|
} |
1540 |
|
} |
1541 |
|
|
1542 |
0 |
if (isLast) { |
1543 |
0 |
stop = 0; |
1544 |
0 |
direction = -1; |
1545 |
|
} else { |
1546 |
0 |
stop = length - 1; |
1547 |
0 |
direction = 1; |
1548 |
|
} |
1549 |
|
|
1550 |
0 |
for (;;) { |
1551 |
0 |
jsid id; |
1552 |
0 |
jsval v; |
1553 |
|
|
1554 |
0 |
if (!IndexToExistingId(cx, obj, (jsuint)i, &id)) |
1555 |
0 |
return JS_FALSE; |
1556 |
0 |
if (id != JSID_HOLE) { |
1557 |
0 |
if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) |
1558 |
0 |
return JS_FALSE; |
1559 |
0 |
if (js_StrictlyEqual(v, argv[0])) |
1560 |
0 |
return js_NewNumberValue(cx, i, rval); |
1561 |
|
} |
1562 |
|
|
1563 |
0 |
if (i == stop) |
1564 |
0 |
goto not_found; |
1565 |
0 |
i += direction; |
1566 |
|
} |
1567 |
|
|
1568 |
|
not_found: |
1569 |
0 |
*rval = INT_TO_JSVAL(-1); |
1570 |
0 |
return JS_TRUE; |
1571 |
|
} |
1572 |
|
|
1573 |
|
static JSBool |
1574 |
|
array_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
1575 |
|
jsval *rval) |
1576 |
0 |
{ |
1577 |
0 |
return array_indexOfHelper(cx, obj, argc, argv, rval, JS_FALSE); |
1578 |
|
} |
1579 |
|
|
1580 |
|
static JSBool |
1581 |
|
array_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
1582 |
|
jsval *rval) |
1583 |
0 |
{ |
1584 |
0 |
return array_indexOfHelper(cx, obj, argc, argv, rval, JS_TRUE); |
1585 |
|
} |
1586 |
|
|
1587 |
|
/* Order is important; extras that use a caller's predicate must follow MAP. */ |
1588 |
|
typedef enum ArrayExtraMode { |
1589 |
|
FOREACH, |
1590 |
|
MAP, |
1591 |
|
FILTER, |
1592 |
|
SOME, |
1593 |
|
EVERY |
1594 |
|
} ArrayExtraMode; |
1595 |
|
|
1596 |
|
static JSBool |
1597 |
|
array_extra(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, |
1598 |
|
ArrayExtraMode mode) |
1599 |
0 |
{ |
1600 |
0 |
jsval *vp, *sp, *origsp, *oldsp; |
1601 |
0 |
jsuint length, newlen, i; |
1602 |
0 |
JSObject *callable, *thisp, *newarr; |
1603 |
0 |
void *mark; |
1604 |
0 |
JSStackFrame *fp; |
1605 |
0 |
JSBool ok, b; |
1606 |
|
|
1607 |
|
/* Hoist the explicit local root address computation. */ |
1608 |
0 |
vp = argv + argc; |
1609 |
|
|
1610 |
0 |
if (!js_GetLengthProperty(cx, obj, &length)) |
1611 |
0 |
return JS_FALSE; |
1612 |
|
|
1613 |
|
/* |
1614 |
|
* First, get or compute our callee, so that we error out consistently |
1615 |
|
* when passed a non-callable object. |
1616 |
|
*/ |
1617 |
0 |
callable = js_ValueToCallableObject(cx, &argv[0], 0); |
1618 |
0 |
if (!callable) |
1619 |
0 |
return JS_FALSE; |
1620 |
|
|
1621 |
|
/* |
1622 |
|
* Set our initial return condition, used for zero-length array cases |
1623 |
|
* (and pre-size our map return to match our known length, for all cases). |
1624 |
|
*/ |
1625 |
|
#ifdef __GNUC__ /* quell GCC overwarning */ |
1626 |
0 |
newlen = 0; |
1627 |
0 |
newarr = NULL; |
1628 |
0 |
ok = JS_TRUE; |
1629 |
|
#endif |
1630 |
0 |
switch (mode) { |
1631 |
|
case MAP: |
1632 |
|
case FILTER: |
1633 |
0 |
newlen = (mode == MAP) ? length : 0; |
1634 |
0 |
newarr = js_NewArrayObject(cx, newlen, NULL); |
1635 |
0 |
if (!newarr) |
1636 |
0 |
return JS_FALSE; |
1637 |
0 |
*rval = OBJECT_TO_JSVAL(newarr); |
1638 |
0 |
break; |
1639 |
|
case SOME: |
1640 |
0 |
*rval = JSVAL_FALSE; |
1641 |
0 |
break; |
1642 |
|
case EVERY: |
1643 |
0 |
*rval = JSVAL_TRUE; |
1644 |
|
break; |
1645 |
|
case FOREACH: |
1646 |
0 |
break; |
1647 |
|
} |
1648 |
|
|
1649 |
0 |
if (length == 0) |
1650 |
0 |
return JS_TRUE; |
1651 |
|
|
1652 |
0 |
if (argc > 1) { |
1653 |
0 |
if (!js_ValueToObject(cx, argv[1], &thisp)) |
1654 |
0 |
return JS_FALSE; |
1655 |
0 |
argv[1] = OBJECT_TO_JSVAL(thisp); |
1656 |
|
} else { |
1657 |
0 |
thisp = NULL; |
1658 |
|
} |
1659 |
|
|
1660 |
|
/* We call with 3 args (value, index, array), plus room for rval. */ |
1661 |
0 |
origsp = js_AllocStack(cx, 2 + 3 + 1, &mark); |
1662 |
0 |
if (!origsp) |
1663 |
0 |
return JS_FALSE; |
1664 |
|
|
1665 |
|
/* Lift current frame to include our args. */ |
1666 |
0 |
fp = cx->fp; |
1667 |
0 |
oldsp = fp->sp; |
1668 |
|
|
1669 |
0 |
for (i = 0; i < length; i++) { |
1670 |
0 |
jsid id; |
1671 |
0 |
jsval rval2; |
1672 |
|
|
1673 |
0 |
ok = IndexToExistingId(cx, obj, i, &id); |
1674 |
0 |
if (!ok) |
1675 |
0 |
break; |
1676 |
0 |
if (id == JSID_HOLE) |
1677 |
0 |
continue; |
1678 |
0 |
ok = OBJ_GET_PROPERTY(cx, obj, id, vp); |
1679 |
0 |
if (!ok) |
1680 |
0 |
break; |
1681 |
|
|
1682 |
|
/* |
1683 |
|
* Push callable and 'this', then args. We must do this for every |
1684 |
|
* iteration around the loop since js_Invoke uses origsp[0] for rval |
1685 |
|
* storage and some native functions use origsp[1] for local rooting. |
1686 |
|
*/ |
1687 |
0 |
sp = origsp; |
1688 |
0 |
*sp++ = OBJECT_TO_JSVAL(callable); |
1689 |
0 |
*sp++ = OBJECT_TO_JSVAL(thisp); |
1690 |
0 |
*sp++ = *vp; |
1691 |
0 |
*sp++ = INT_TO_JSVAL(i); |
1692 |
0 |
*sp++ = OBJECT_TO_JSVAL(obj); |
1693 |
|
|
1694 |
|
/* Do the call. */ |
1695 |
0 |
fp->sp = sp; |
1696 |
0 |
ok = js_Invoke(cx, 3, JSINVOKE_INTERNAL); |
1697 |
0 |
rval2 = fp->sp[-1]; |
1698 |
0 |
fp->sp = oldsp; |
1699 |
0 |
if (!ok) |
1700 |
0 |
break; |
1701 |
|
|
1702 |
0 |
if (mode > MAP) { |
1703 |
0 |
if (rval2 == JSVAL_NULL) { |
1704 |
0 |
b = JS_FALSE; |
1705 |
0 |
} else if (JSVAL_IS_BOOLEAN(rval2)) { |
1706 |
0 |
b = JSVAL_TO_BOOLEAN(rval2); |
1707 |
|
} else { |
1708 |
0 |
ok = js_ValueToBoolean(cx, rval2, &b); |
1709 |
0 |
if (!ok) |
1710 |
0 |
goto out; |
1711 |
|
} |
1712 |
|
} |
1713 |
|
|
1714 |
0 |
switch (mode) { |
1715 |
|
case FOREACH: |
1716 |
0 |
break; |
1717 |
|
case MAP: |
1718 |
|
/* |
1719 |
|
* We reconstruct id once again when it is a GC thing since scripts |
1720 |
|
* can trigger GC that collects it. See bug 341956. |
1721 |
|
*/ |
1722 |
0 |
if (i > JSVAL_INT_MAX) { |
1723 |
0 |
ok = IndexToId(cx, i, &id); |
1724 |
0 |
if (!ok) |
1725 |
0 |
goto out; |
1726 |
|
} |
1727 |
0 |
ok = OBJ_SET_PROPERTY(cx, newarr, id, &rval2); |
1728 |
0 |
if (!ok) |
1729 |
0 |
goto out; |
1730 |
0 |
break; |
1731 |
|
case FILTER: |
1732 |
0 |
if (!b) |
1733 |
0 |
break; |
1734 |
|
/* Filter passed *vp, push as result. */ |
1735 |
0 |
ok = IndexToId(cx, newlen++, &id); |
1736 |
0 |
if (!ok) |
1737 |
0 |
goto out; |
1738 |
0 |
ok = OBJ_SET_PROPERTY(cx, newarr, id, vp); |
1739 |
0 |
if (!ok) |
1740 |
0 |
goto out; |
1741 |
0 |
break; |
1742 |
|
case SOME: |
1743 |
0 |
if (b) { |
1744 |
0 |
*rval = JSVAL_TRUE; |
1745 |
0 |
goto out; |
1746 |
|
} |
1747 |
0 |
break; |
1748 |
|
case EVERY: |
1749 |
0 |
if (!b) { |
1750 |
0 |
*rval = JSVAL_FALSE; |
1751 |
0 |
goto out; |
1752 |
|
} |
1753 |
0 |
break; |
1754 |
|
} |
1755 |
|
} |
1756 |
|
|
1757 |
|
out: |
1758 |
0 |
js_FreeStack(cx, mark); |
1759 |
0 |
if (ok && mode == FILTER) |
1760 |
0 |
ok = js_SetLengthProperty(cx, newarr, newlen); |
1761 |
0 |
return ok; |
1762 |
|
} |
1763 |
|
|
1764 |
|
static JSBool |
1765 |
|
array_forEach(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
1766 |
|
jsval *rval) |
1767 |
0 |
{ |
1768 |
0 |
return array_extra(cx, obj, argc, argv, rval, FOREACH); |
1769 |
|
} |
1770 |
|
|
1771 |
|
static JSBool |
1772 |
|
array_map(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
1773 |
|
jsval *rval) |
1774 |
0 |
{ |
1775 |
0 |
return array_extra(cx, obj, argc, argv, rval, MAP); |
1776 |
|
} |
1777 |
|
|
1778 |
|
static JSBool |
1779 |
|
array_filter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
1780 |
|
jsval *rval) |
1781 |
0 |
{ |
1782 |
0 |
return array_extra(cx, obj, argc, argv, rval, FILTER); |
1783 |
|
} |
1784 |
|
|
1785 |
|
static JSBool |
1786 |
|
array_some(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
1787 |
|
jsval *rval) |
1788 |
0 |
{ |
1789 |
0 |
return array_extra(cx, obj, argc, argv, rval, SOME); |
1790 |
|
} |
1791 |
|
|
1792 |
|
static JSBool |
1793 |
|
array_every(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
1794 |
|
jsval *rval) |
1795 |
0 |
{ |
1796 |
0 |
return array_extra(cx, obj, argc, argv, rval, EVERY); |
1797 |
|
} |
1798 |
|
#endif |
1799 |
|
|
1800 |
|
static JSFunctionSpec array_methods[] = { |
1801 |
|
#if JS_HAS_TOSOURCE |
1802 |
|
{js_toSource_str, array_toSource, 0,0,0}, |
1803 |
|
#endif |
1804 |
|
{js_toString_str, array_toString, 0,0,0}, |
1805 |
|
{js_toLocaleString_str, array_toLocaleString, 0,0,0}, |
1806 |
|
|
1807 |
|
/* Perl-ish methods. */ |
1808 |
|
#if JS_HAS_SOME_PERL_FUN |
1809 |
|
{"join", array_join, 1,JSFUN_GENERIC_NATIVE,0}, |
1810 |
|
{"reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE,2}, |
1811 |
|
{"sort", array_sort, 1,JSFUN_GENERIC_NATIVE,2}, |
1812 |
|
#endif |
1813 |
|
#if JS_HAS_MORE_PERL_FUN |
1814 |
|
{"push", array_push, 1,JSFUN_GENERIC_NATIVE,0}, |
1815 |
|
{"pop", array_pop, 0,JSFUN_GENERIC_NATIVE,0}, |
1816 |
|
{"shift", array_shift, 0,JSFUN_GENERIC_NATIVE,1}, |
1817 |
|
{"unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE,1}, |
1818 |
|
{"splice", array_splice, 2,JSFUN_GENERIC_NATIVE,1}, |
1819 |
|
#endif |
1820 |
|
|
1821 |
|
/* Python-esque sequence methods. */ |
1822 |
|
#if JS_HAS_SEQUENCE_OPS |
1823 |
|
{"concat", array_concat, 1,JSFUN_GENERIC_NATIVE,1}, |
1824 |
|
{"slice", array_slice, 2,JSFUN_GENERIC_NATIVE,1}, |
1825 |
|
#endif |
1826 |
|
|
1827 |
|
#if JS_HAS_ARRAY_EXTRAS |
1828 |
|
{"indexOf", array_indexOf, 1,JSFUN_GENERIC_NATIVE,0}, |
1829 |
|
{"lastIndexOf", array_lastIndexOf, 1,JSFUN_GENERIC_NATIVE,0}, |
1830 |
|
{"forEach", array_forEach, 1,JSFUN_GENERIC_NATIVE,1}, |
1831 |
|
{"map", array_map, 1,JSFUN_GENERIC_NATIVE,1}, |
1832 |
|
{"filter", array_filter, 1,JSFUN_GENERIC_NATIVE,1}, |
1833 |
|
{"some", array_some, 1,JSFUN_GENERIC_NATIVE,1}, |
1834 |
|
{"every", array_every, 1,JSFUN_GENERIC_NATIVE,1}, |
1835 |
|
#endif |
1836 |
|
|
1837 |
|
{0,0,0,0,0} |
1838 |
|
}; |
1839 |
|
|
1840 |
|
static JSBool |
1841 |
|
Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
1842 |
44474 |
{ |
1843 |
44474 |
jsuint length; |
1844 |
44474 |
jsval *vector; |
1845 |
|
|
1846 |
|
/* If called without new, replace obj with a new Array object. */ |
1847 |
44474 |
if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { |
1848 |
0 |
obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); |
1849 |
0 |
if (!obj) |
1850 |
0 |
return JS_FALSE; |
1851 |
0 |
*rval = OBJECT_TO_JSVAL(obj); |
1852 |
|
} |
1853 |
|
|
1854 |
44474 |
if (argc == 0) { |
1855 |
44474 |
length = 0; |
1856 |
44474 |
vector = NULL; |
1857 |
0 |
} else if (JS_VERSION_IS_1_2(cx)) { |
1858 |
0 |
length = (jsuint) argc; |
1859 |
0 |
vector = argv; |
1860 |
0 |
} else if (argc > 1) { |
1861 |
0 |
length = (jsuint) argc; |
1862 |
0 |
vector = argv; |
1863 |
0 |
} else if (!JSVAL_IS_NUMBER(argv[0])) { |
1864 |
0 |
length = 1; |
1865 |
0 |
vector = argv; |
1866 |
|
} else { |
1867 |
0 |
if (!ValueIsLength(cx, argv[0], &length)) |
1868 |
0 |
return JS_FALSE; |
1869 |
0 |
vector = NULL; |
1870 |
|
} |
1871 |
44474 |
return InitArrayObject(cx, obj, length, vector); |
1872 |
|
} |
1873 |
|
|
1874 |
|
JSObject * |
1875 |
|
js_InitArrayClass(JSContext *cx, JSObject *obj) |
1876 |
16 |
{ |
1877 |
16 |
JSObject *proto; |
1878 |
|
|
1879 |
16 |
proto = JS_InitClass(cx, obj, NULL, &js_ArrayClass, Array, 1, |
1880 |
|
NULL, array_methods, NULL, NULL); |
1881 |
|
|
1882 |
|
/* Initialize the Array prototype object so it gets a length property. */ |
1883 |
16 |
if (!proto || !InitArrayObject(cx, proto, 0, NULL)) |
1884 |
0 |
return NULL; |
1885 |
16 |
return proto; |
1886 |
|
} |
1887 |
|
|
1888 |
|
JSObject * |
1889 |
|
js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector) |
1890 |
1130 |
{ |
1891 |
1130 |
JSObject *obj; |
1892 |
|
|
1893 |
1130 |
obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); |
1894 |
1130 |
if (!obj) |
1895 |
0 |
return NULL; |
1896 |
1130 |
if (!InitArrayObject(cx, obj, length, vector)) { |
1897 |
0 |
cx->newborn[GCX_OBJECT] = NULL; |
1898 |
0 |
return NULL; |
1899 |
|
} |
1900 |
1130 |
return obj; |