Parent Directory
|
Revision Log
Upgrade to SpiderMonkey from Firefox 3.5.3.
1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 | * vim: set ts=8 sw=4 et tw=78: |
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 | * JavaScript API. |
43 | */ |
44 | #include "jsstddef.h" |
45 | #include <ctype.h> |
46 | #include <stdarg.h> |
47 | #include <stdlib.h> |
48 | #include <string.h> |
49 | #include "jstypes.h" |
50 | #include "jsarena.h" /* Added by JSIFY */ |
51 | #include "jsutil.h" /* Added by JSIFY */ |
52 | #include "jsclist.h" |
53 | #include "jsdhash.h" |
54 | #include "jsprf.h" |
55 | #include "jsapi.h" |
56 | #include "jsarray.h" |
57 | #include "jsatom.h" |
58 | #include "jsbool.h" |
59 | #include "jsbuiltins.h" |
60 | #include "jscntxt.h" |
61 | #include "jsversion.h" |
62 | #include "jsdate.h" |
63 | #include "jsdtoa.h" |
64 | #include "jsemit.h" |
65 | #include "jsexn.h" |
66 | #include "jsfun.h" |
67 | #include "jsgc.h" |
68 | #include "jsinterp.h" |
69 | #include "jsiter.h" |
70 | #include "jslock.h" |
71 | #include "jsmath.h" |
72 | #include "jsnum.h" |
73 | #include "json.h" |
74 | #include "jsobj.h" |
75 | #include "jsopcode.h" |
76 | #include "jsparse.h" |
77 | #include "jsregexp.h" |
78 | #include "jsscan.h" |
79 | #include "jsscope.h" |
80 | #include "jsscript.h" |
81 | #include "jsstr.h" |
82 | #include "jstracer.h" |
83 | #include "jsdbgapi.h" |
84 | #include "prmjtime.h" |
85 | #include "jsstaticcheck.h" |
86 | |
87 | #if JS_HAS_FILE_OBJECT |
88 | #include "jsfile.h" |
89 | #endif |
90 | |
91 | #if JS_HAS_XML_SUPPORT |
92 | #include "jsxml.h" |
93 | #endif |
94 | |
95 | #ifdef HAVE_VA_LIST_AS_ARRAY |
96 | #define JS_ADDRESSOF_VA_LIST(ap) ((va_list *)(ap)) |
97 | #else |
98 | #define JS_ADDRESSOF_VA_LIST(ap) (&(ap)) |
99 | #endif |
100 | |
101 | #if defined(JS_THREADSAFE) |
102 | #define CHECK_REQUEST(cx) \ |
103 | JS_ASSERT((cx)->requestDepth || (cx)->thread == (cx)->runtime->gcThread) |
104 | #else |
105 | #define CHECK_REQUEST(cx) ((void)0) |
106 | #endif |
107 | |
108 | /* Check that we can cast JSObject* as jsval without tag bit manipulations. */ |
109 | JS_STATIC_ASSERT(JSVAL_OBJECT == 0); |
110 | |
111 | /* Check that JSVAL_TRACE_KIND works. */ |
112 | JS_STATIC_ASSERT(JSVAL_TRACE_KIND(JSVAL_OBJECT) == JSTRACE_OBJECT); |
113 | JS_STATIC_ASSERT(JSVAL_TRACE_KIND(JSVAL_DOUBLE) == JSTRACE_DOUBLE); |
114 | JS_STATIC_ASSERT(JSVAL_TRACE_KIND(JSVAL_STRING) == JSTRACE_STRING); |
115 | |
116 | JS_PUBLIC_API(int64) |
117 | JS_Now() |
118 | { |
119 | return PRMJ_Now(); |
120 | } |
121 | |
122 | JS_PUBLIC_API(jsval) |
123 | JS_GetNaNValue(JSContext *cx) |
124 | { |
125 | return DOUBLE_TO_JSVAL(cx->runtime->jsNaN); |
126 | } |
127 | |
128 | JS_PUBLIC_API(jsval) |
129 | JS_GetNegativeInfinityValue(JSContext *cx) |
130 | { |
131 | return DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity); |
132 | } |
133 | |
134 | JS_PUBLIC_API(jsval) |
135 | JS_GetPositiveInfinityValue(JSContext *cx) |
136 | { |
137 | return DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity); |
138 | } |
139 | |
140 | JS_PUBLIC_API(jsval) |
141 | JS_GetEmptyStringValue(JSContext *cx) |
142 | { |
143 | return STRING_TO_JSVAL(cx->runtime->emptyString); |
144 | } |
145 | |
146 | static JSBool |
147 | TryArgumentFormatter(JSContext *cx, const char **formatp, JSBool fromJS, |
148 | jsval **vpp, va_list *app) |
149 | { |
150 | const char *format; |
151 | JSArgumentFormatMap *map; |
152 | |
153 | format = *formatp; |
154 | for (map = cx->argumentFormatMap; map; map = map->next) { |
155 | if (!strncmp(format, map->format, map->length)) { |
156 | *formatp = format + map->length; |
157 | return map->formatter(cx, format, fromJS, vpp, app); |
158 | } |
159 | } |
160 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CHAR, format); |
161 | return JS_FALSE; |
162 | } |
163 | |
164 | JS_PUBLIC_API(JSBool) |
165 | JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, |
166 | ...) |
167 | { |
168 | va_list ap; |
169 | JSBool ok; |
170 | |
171 | va_start(ap, format); |
172 | ok = JS_ConvertArgumentsVA(cx, argc, argv, format, ap); |
173 | va_end(ap); |
174 | return ok; |
175 | } |
176 | |
177 | JS_PUBLIC_API(JSBool) |
178 | JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, |
179 | const char *format, va_list ap) |
180 | { |
181 | jsval *sp; |
182 | JSBool required; |
183 | char c; |
184 | JSFunction *fun; |
185 | jsdouble d; |
186 | JSString *str; |
187 | JSObject *obj; |
188 | |
189 | CHECK_REQUEST(cx); |
190 | sp = argv; |
191 | required = JS_TRUE; |
192 | while ((c = *format++) != '\0') { |
193 | if (isspace(c)) |
194 | continue; |
195 | if (c == '/') { |
196 | required = JS_FALSE; |
197 | continue; |
198 | } |
199 | if (sp == argv + argc) { |
200 | if (required) { |
201 | fun = js_ValueToFunction(cx, &argv[-2], 0); |
202 | if (fun) { |
203 | char numBuf[12]; |
204 | JS_snprintf(numBuf, sizeof numBuf, "%u", argc); |
205 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
206 | JSMSG_MORE_ARGS_NEEDED, |
207 | JS_GetFunctionName(fun), numBuf, |
208 | (argc == 1) ? "" : "s"); |
209 | } |
210 | return JS_FALSE; |
211 | } |
212 | break; |
213 | } |
214 | switch (c) { |
215 | case 'b': |
216 | *va_arg(ap, JSBool *) = js_ValueToBoolean(*sp); |
217 | break; |
218 | case 'c': |
219 | if (!JS_ValueToUint16(cx, *sp, va_arg(ap, uint16 *))) |
220 | return JS_FALSE; |
221 | break; |
222 | case 'i': |
223 | if (!JS_ValueToECMAInt32(cx, *sp, va_arg(ap, int32 *))) |
224 | return JS_FALSE; |
225 | break; |
226 | case 'u': |
227 | if (!JS_ValueToECMAUint32(cx, *sp, va_arg(ap, uint32 *))) |
228 | return JS_FALSE; |
229 | break; |
230 | case 'j': |
231 | if (!JS_ValueToInt32(cx, *sp, va_arg(ap, int32 *))) |
232 | return JS_FALSE; |
233 | break; |
234 | case 'd': |
235 | if (!JS_ValueToNumber(cx, *sp, va_arg(ap, jsdouble *))) |
236 | return JS_FALSE; |
237 | break; |
238 | case 'I': |
239 | if (!JS_ValueToNumber(cx, *sp, &d)) |
240 | return JS_FALSE; |
241 | *va_arg(ap, jsdouble *) = js_DoubleToInteger(d); |
242 | break; |
243 | case 's': |
244 | case 'S': |
245 | case 'W': |
246 | str = js_ValueToString(cx, *sp); |
247 | if (!str) |
248 | return JS_FALSE; |
249 | *sp = STRING_TO_JSVAL(str); |
250 | if (c == 's') { |
251 | const char *bytes = js_GetStringBytes(cx, str); |
252 | if (!bytes) |
253 | return JS_FALSE; |
254 | *va_arg(ap, const char **) = bytes; |
255 | } else if (c == 'W') { |
256 | const jschar *chars = js_GetStringChars(cx, str); |
257 | if (!chars) |
258 | return JS_FALSE; |
259 | *va_arg(ap, const jschar **) = chars; |
260 | } else { |
261 | *va_arg(ap, JSString **) = str; |
262 | } |
263 | break; |
264 | case 'o': |
265 | if (!js_ValueToObject(cx, *sp, &obj)) |
266 | return JS_FALSE; |
267 | *sp = OBJECT_TO_JSVAL(obj); |
268 | *va_arg(ap, JSObject **) = obj; |
269 | break; |
270 | case 'f': |
271 | obj = js_ValueToFunctionObject(cx, sp, 0); |
272 | if (!obj) |
273 | return JS_FALSE; |
274 | *sp = OBJECT_TO_JSVAL(obj); |
275 | *va_arg(ap, JSFunction **) = (JSFunction *) JS_GetPrivate(cx, obj); |
276 | break; |
277 | case 'v': |
278 | *va_arg(ap, jsval *) = *sp; |
279 | break; |
280 | case '*': |
281 | break; |
282 | default: |
283 | format--; |
284 | if (!TryArgumentFormatter(cx, &format, JS_TRUE, &sp, |
285 | JS_ADDRESSOF_VA_LIST(ap))) { |
286 | return JS_FALSE; |
287 | } |
288 | /* NB: the formatter already updated sp, so we continue here. */ |
289 | continue; |
290 | } |
291 | sp++; |
292 | } |
293 | return JS_TRUE; |
294 | } |
295 | |
296 | JS_PUBLIC_API(jsval *) |
297 | JS_PushArguments(JSContext *cx, void **markp, const char *format, ...) |
298 | { |
299 | va_list ap; |
300 | jsval *argv; |
301 | |
302 | va_start(ap, format); |
303 | argv = JS_PushArgumentsVA(cx, markp, format, ap); |
304 | va_end(ap); |
305 | return argv; |
306 | } |
307 | |
308 | JS_PUBLIC_API(jsval *) |
309 | JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap) |
310 | { |
311 | uintN argc; |
312 | jsval *argv, *sp; |
313 | char c; |
314 | const char *cp; |
315 | JSString *str; |
316 | JSFunction *fun; |
317 | JSStackHeader *sh; |
318 | |
319 | CHECK_REQUEST(cx); |
320 | *markp = NULL; |
321 | argc = 0; |
322 | for (cp = format; (c = *cp) != '\0'; cp++) { |
323 | /* |
324 | * Count non-space non-star characters as individual jsval arguments. |
325 | * This may over-allocate stack, but we'll fix below. |
326 | */ |
327 | if (isspace(c) || c == '*') |
328 | continue; |
329 | argc++; |
330 | } |
331 | js_LeaveTrace(cx); |
332 | sp = js_AllocStack(cx, argc, markp); |
333 | if (!sp) |
334 | return NULL; |
335 | argv = sp; |
336 | while ((c = *format++) != '\0') { |
337 | if (isspace(c) || c == '*') |
338 | continue; |
339 | switch (c) { |
340 | case 'b': |
341 | *sp = BOOLEAN_TO_JSVAL((JSBool) va_arg(ap, int)); |
342 | break; |
343 | case 'c': |
344 | *sp = INT_TO_JSVAL((uint16) va_arg(ap, unsigned int)); |
345 | break; |
346 | case 'i': |
347 | case 'j': |
348 | /* |
349 | * Use JS_New{Double,Number}Value here and in the next two cases, |
350 | * not js_New{Double,Number}InRootedValue, as sp may point to an |
351 | * unrooted location. |
352 | */ |
353 | if (!JS_NewNumberValue(cx, (jsdouble) va_arg(ap, int32), sp)) |
354 | goto bad; |
355 | break; |
356 | case 'u': |
357 | if (!JS_NewNumberValue(cx, (jsdouble) va_arg(ap, uint32), sp)) |
358 | goto bad; |
359 | break; |
360 | case 'd': |
361 | case 'I': |
362 | if (!JS_NewDoubleValue(cx, va_arg(ap, jsdouble), sp)) |
363 | goto bad; |
364 | break; |
365 | case 's': |
366 | str = JS_NewStringCopyZ(cx, va_arg(ap, char *)); |
367 | if (!str) |
368 | goto bad; |
369 | *sp = STRING_TO_JSVAL(str); |
370 | break; |
371 | case 'W': |
372 | str = JS_NewUCStringCopyZ(cx, va_arg(ap, jschar *)); |
373 | if (!str) |
374 | goto bad; |
375 | *sp = STRING_TO_JSVAL(str); |
376 | break; |
377 | case 'S': |
378 | str = va_arg(ap, JSString *); |
379 | *sp = STRING_TO_JSVAL(str); |
380 | break; |
381 | case 'o': |
382 | *sp = OBJECT_TO_JSVAL(va_arg(ap, JSObject *)); |
383 | break; |
384 | case 'f': |
385 | fun = va_arg(ap, JSFunction *); |
386 | *sp = fun ? OBJECT_TO_JSVAL(FUN_OBJECT(fun)) : JSVAL_NULL; |
387 | break; |
388 | case 'v': |
389 | *sp = va_arg(ap, jsval); |
390 | break; |
391 | default: |
392 | format--; |
393 | if (!TryArgumentFormatter(cx, &format, JS_FALSE, &sp, |
394 | JS_ADDRESSOF_VA_LIST(ap))) { |
395 | goto bad; |
396 | } |
397 | /* NB: the formatter already updated sp, so we continue here. */ |
398 | continue; |
399 | } |
400 | sp++; |
401 | } |
402 | |
403 | /* |
404 | * We may have overallocated stack due to a multi-character format code |
405 | * handled by a JSArgumentFormatter. Give back that stack space! |
406 | */ |
407 | JS_ASSERT(sp <= argv + argc); |
408 | if (sp < argv + argc) { |
409 | /* Return slots not pushed to the current stack arena. */ |
410 | cx->stackPool.current->avail = (jsuword)sp; |
411 | |
412 | /* Reduce the count of slots the GC will scan in this stack segment. */ |
413 | sh = cx->stackHeaders; |
414 | JS_ASSERT(JS_STACK_SEGMENT(sh) + sh->nslots == argv + argc); |
415 | sh->nslots -= argc - (sp - argv); |
416 | } |
417 | return argv; |
418 | |
419 | bad: |
420 | js_FreeStack(cx, *markp); |
421 | return NULL; |
422 | } |
423 | |
424 | JS_PUBLIC_API(void) |
425 | JS_PopArguments(JSContext *cx, void *mark) |
426 | { |
427 | CHECK_REQUEST(cx); |
428 | JS_ASSERT_NOT_ON_TRACE(cx); |
429 | js_FreeStack(cx, mark); |
430 | } |
431 | |
432 | JS_PUBLIC_API(JSBool) |
433 | JS_AddArgumentFormatter(JSContext *cx, const char *format, |
434 | JSArgumentFormatter formatter) |
435 | { |
436 | size_t length; |
437 | JSArgumentFormatMap **mpp, *map; |
438 | |
439 | length = strlen(format); |
440 | mpp = &cx->argumentFormatMap; |
441 | while ((map = *mpp) != NULL) { |
442 | /* Insert before any shorter string to match before prefixes. */ |
443 | if (map->length < length) |
444 | break; |
445 | if (map->length == length && !strcmp(map->format, format)) |
446 | goto out; |
447 | mpp = &map->next; |
448 | } |
449 | map = (JSArgumentFormatMap *) JS_malloc(cx, sizeof *map); |
450 | if (!map) |
451 | return JS_FALSE; |
452 | map->format = format; |
453 | map->length = length; |
454 | map->next = *mpp; |
455 | *mpp = map; |
456 | out: |
457 | map->formatter = formatter; |
458 | return JS_TRUE; |
459 | } |
460 | |
461 | JS_PUBLIC_API(void) |
462 | JS_RemoveArgumentFormatter(JSContext *cx, const char *format) |
463 | { |
464 | size_t length; |
465 | JSArgumentFormatMap **mpp, *map; |
466 | |
467 | length = strlen(format); |
468 | mpp = &cx->argumentFormatMap; |
469 | while ((map = *mpp) != NULL) { |
470 | if (map->length == length && !strcmp(map->format, format)) { |
471 | *mpp = map->next; |
472 | JS_free(cx, map); |
473 | return; |
474 | } |
475 | mpp = &map->next; |
476 | } |
477 | } |
478 | |
479 | JS_PUBLIC_API(JSBool) |
480 | JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) |
481 | { |
482 | JSBool ok; |
483 | JSObject *obj; |
484 | JSString *str; |
485 | jsdouble d, *dp; |
486 | |
487 | CHECK_REQUEST(cx); |
488 | switch (type) { |
489 | case JSTYPE_VOID: |
490 | *vp = JSVAL_VOID; |
491 | ok = JS_TRUE; |
492 | break; |
493 | case JSTYPE_OBJECT: |
494 | ok = js_ValueToObject(cx, v, &obj); |
495 | if (ok) |
496 | *vp = OBJECT_TO_JSVAL(obj); |
497 | break; |
498 | case JSTYPE_FUNCTION: |
499 | *vp = v; |
500 | obj = js_ValueToFunctionObject(cx, vp, JSV2F_SEARCH_STACK); |
501 | ok = (obj != NULL); |
502 | break; |
503 | case JSTYPE_STRING: |
504 | str = js_ValueToString(cx, v); |
505 | ok = (str != NULL); |
506 | if (ok) |
507 | *vp = STRING_TO_JSVAL(str); |
508 | break; |
509 | case JSTYPE_NUMBER: |
510 | ok = JS_ValueToNumber(cx, v, &d); |
511 | if (ok) { |
512 | dp = js_NewWeaklyRootedDouble(cx, d); |
513 | ok = (dp != NULL); |
514 | if (ok) |
515 | *vp = DOUBLE_TO_JSVAL(dp); |
516 | } |
517 | break; |
518 | case JSTYPE_BOOLEAN: |
519 | *vp = BOOLEAN_TO_JSVAL(js_ValueToBoolean(v)); |
520 | return JS_TRUE; |
521 | default: { |
522 | char numBuf[12]; |
523 | JS_snprintf(numBuf, sizeof numBuf, "%d", (int)type); |
524 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_TYPE, |
525 | numBuf); |
526 | ok = JS_FALSE; |
527 | break; |
528 | } |
529 | } |
530 | return ok; |
531 | } |
532 | |
533 | JS_PUBLIC_API(JSBool) |
534 | JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp) |
535 | { |
536 | CHECK_REQUEST(cx); |
537 | return js_ValueToObject(cx, v, objp); |
538 | } |
539 | |
540 | JS_PUBLIC_API(JSFunction *) |
541 | JS_ValueToFunction(JSContext *cx, jsval v) |
542 | { |
543 | CHECK_REQUEST(cx); |
544 | return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK); |
545 | } |
546 | |
547 | JS_PUBLIC_API(JSFunction *) |
548 | JS_ValueToConstructor(JSContext *cx, jsval v) |
549 | { |
550 | CHECK_REQUEST(cx); |
551 | return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK); |
552 | } |
553 | |
554 | JS_PUBLIC_API(JSString *) |
555 | JS_ValueToString(JSContext *cx, jsval v) |
556 | { |
557 | CHECK_REQUEST(cx); |
558 | return js_ValueToString(cx, v); |
559 | } |
560 | |
561 | JS_PUBLIC_API(JSString *) |
562 | JS_ValueToSource(JSContext *cx, jsval v) |
563 | { |
564 | CHECK_REQUEST(cx); |
565 | return js_ValueToSource(cx, v); |
566 | } |
567 | |
568 | JS_PUBLIC_API(JSBool) |
569 | JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) |
570 | { |
571 | JSTempValueRooter tvr; |
572 | |
573 | CHECK_REQUEST(cx); |
574 | JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr); |
575 | *dp = js_ValueToNumber(cx, &tvr.u.value); |
576 | JS_POP_TEMP_ROOT(cx, &tvr); |
577 | return !JSVAL_IS_NULL(tvr.u.value); |
578 | } |
579 | |
580 | JS_PUBLIC_API(JSBool) |
581 | JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip) |
582 | { |
583 | JSTempValueRooter tvr; |
584 | |
585 | CHECK_REQUEST(cx); |
586 | JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr); |
587 | *ip = js_ValueToECMAInt32(cx, &tvr.u.value); |
588 | JS_POP_TEMP_ROOT(cx, &tvr); |
589 | return !JSVAL_IS_NULL(tvr.u.value); |
590 | } |
591 | |
592 | JS_PUBLIC_API(JSBool) |
593 | JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip) |
594 | { |
595 | JSTempValueRooter tvr; |
596 | |
597 | CHECK_REQUEST(cx); |
598 | JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr); |
599 | *ip = js_ValueToECMAUint32(cx, &tvr.u.value); |
600 | JS_POP_TEMP_ROOT(cx, &tvr); |
601 | return !JSVAL_IS_NULL(tvr.u.value); |
602 | } |
603 | |
604 | JS_PUBLIC_API(JSBool) |
605 | JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip) |
606 | { |
607 | JSTempValueRooter tvr; |
608 | |
609 | CHECK_REQUEST(cx); |
610 | JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr); |
611 | *ip = js_ValueToInt32(cx, &tvr.u.value); |
612 | JS_POP_TEMP_ROOT(cx, &tvr); |
613 | return !JSVAL_IS_NULL(tvr.u.value); |
614 | } |
615 | |
616 | JS_PUBLIC_API(JSBool) |
617 | JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip) |
618 | { |
619 | JSTempValueRooter tvr; |
620 | |
621 | CHECK_REQUEST(cx); |
622 | JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr); |
623 | *ip = js_ValueToUint16(cx, &tvr.u.value); |
624 | JS_POP_TEMP_ROOT(cx, &tvr); |
625 | return !JSVAL_IS_NULL(tvr.u.value); |
626 | } |
627 | |
628 | JS_PUBLIC_API(JSBool) |
629 | JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) |
630 | { |
631 | CHECK_REQUEST(cx); |
632 | *bp = js_ValueToBoolean(v); |
633 | return JS_TRUE; |
634 | } |
635 | |
636 | JS_PUBLIC_API(JSType) |
637 | JS_TypeOfValue(JSContext *cx, jsval v) |
638 | { |
639 | JSType type; |
640 | JSObject *obj; |
641 | JSObjectOps *ops; |
642 | JSClass *clasp; |
643 | |
644 | CHECK_REQUEST(cx); |
645 | if (JSVAL_IS_OBJECT(v)) { |
646 | type = JSTYPE_OBJECT; /* XXXbe JSTYPE_NULL for JS2 */ |
647 | obj = JSVAL_TO_OBJECT(v); |
648 | if (obj) { |
649 | obj = js_GetWrappedObject(cx, obj); |
650 | |
651 | ops = obj->map->ops; |
652 | #if JS_HAS_XML_SUPPORT |
653 | if (ops == &js_XMLObjectOps) { |
654 | type = JSTYPE_XML; |
655 | } else |
656 | #endif |
657 | { |
658 | /* |
659 | * ECMA 262, 11.4.3 says that any native object that implements |
660 | * [[Call]] should be of type "function". However, RegExp is of |
661 | * type "object", not "function", for Web compatibility. |
662 | */ |
663 | clasp = OBJ_GET_CLASS(cx, obj); |
664 | if ((ops == &js_ObjectOps) |
665 | ? (clasp->call |
666 | ? clasp == &js_ScriptClass |
667 | : clasp == &js_FunctionClass) |
668 | : ops->call != NULL) { |
669 | type = JSTYPE_FUNCTION; |
670 | } else { |
671 | #ifdef NARCISSUS |
672 | JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); |
673 | |
674 | if (!OBJ_GET_PROPERTY(cx, obj, |
675 | ATOM_TO_JSID(cx->runtime->atomState |
676 | .__call__Atom), |
677 | &v)) { |
678 | JS_ClearPendingException(cx); |
679 | } else if (VALUE_IS_FUNCTION(cx, v)) { |
680 | type = JSTYPE_FUNCTION; |
681 | } |
682 | #endif |
683 | } |
684 | } |
685 | } |
686 | } else if (JSVAL_IS_NUMBER(v)) { |
687 | type = JSTYPE_NUMBER; |
688 | } else if (JSVAL_IS_STRING(v)) { |
689 | type = JSTYPE_STRING; |
690 | } else if (JSVAL_IS_BOOLEAN(v)) { |
691 | type = JSTYPE_BOOLEAN; |
692 | } else { |
693 | type = JSTYPE_VOID; |
694 | } |
695 | return type; |
696 | } |
697 | |
698 | JS_PUBLIC_API(const char *) |
699 | JS_GetTypeName(JSContext *cx, JSType type) |
700 | { |
701 | if ((uintN)type >= (uintN)JSTYPE_LIMIT) |
702 | return NULL; |
703 | return JS_TYPE_STR(type); |
704 | } |
705 | |
706 | /************************************************************************/ |
707 | |
708 | /* |
709 | * Has a new runtime ever been created? This flag is used to detect unsafe |
710 | * changes to js_CStringsAreUTF8 after a runtime has been created, and to |
711 | * ensure that "first checks" on runtime creation are run only once. |
712 | */ |
713 | #ifdef DEBUG |
714 | static JSBool js_NewRuntimeWasCalled = JS_FALSE; |
715 | #endif |
716 | |
717 | JS_PUBLIC_API(JSRuntime *) |
718 | JS_NewRuntime(uint32 maxbytes) |
719 | { |
720 | JSRuntime *rt; |
721 | |
722 | #ifdef DEBUG |
723 | if (!js_NewRuntimeWasCalled) { |
724 | /* |
725 | * This code asserts that the numbers associated with the error names |
726 | * in jsmsg.def are monotonically increasing. It uses values for the |
727 | * error names enumerated in jscntxt.c. It's not a compile-time check |
728 | * but it's better than nothing. |
729 | */ |
730 | int errorNumber = 0; |
731 | #define MSG_DEF(name, number, count, exception, format) \ |
732 | JS_ASSERT(name == errorNumber++); |
733 | #include "js.msg" |
734 | #undef MSG_DEF |
735 | |
736 | #define MSG_DEF(name, number, count, exception, format) \ |
737 | JS_BEGIN_MACRO \ |
738 | uintN numfmtspecs = 0; \ |
739 | const char *fmt; \ |
740 | for (fmt = format; *fmt != '\0'; fmt++) { \ |
741 | if (*fmt == '{' && isdigit(fmt[1])) \ |
742 | ++numfmtspecs; \ |
743 | } \ |
744 | JS_ASSERT(count == numfmtspecs); \ |
745 | JS_END_MACRO; |
746 | #include "js.msg" |
747 | #undef MSG_DEF |
748 | |
749 | /* |
750 | * If it were possible for pure inline function calls with constant |
751 | * arguments to be computed at compile time, these would be static |
752 | * assertions, but since it isn't, this is the best we can do. |
753 | */ |
754 | JS_ASSERT(JSVAL_NULL == OBJECT_TO_JSVAL(NULL)); |
755 | JS_ASSERT(JSVAL_ZERO == INT_TO_JSVAL(0)); |
756 | JS_ASSERT(JSVAL_ONE == INT_TO_JSVAL(1)); |
757 | JS_ASSERT(JSVAL_FALSE == BOOLEAN_TO_JSVAL(JS_FALSE)); |
758 | JS_ASSERT(JSVAL_TRUE == BOOLEAN_TO_JSVAL(JS_TRUE)); |
759 | |
760 | JS_ASSERT(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID) == 2); |
761 | JS_ASSERT(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_HOLE) == (2 | (JSVAL_HOLE_FLAG >> JSVAL_TAGBITS))); |
762 | JS_ASSERT(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_ARETURN) == 8); |
763 | |
764 | js_NewRuntimeWasCalled = JS_TRUE; |
765 | } |
766 | #endif /* DEBUG */ |
767 | |
768 | rt = (JSRuntime *) malloc(sizeof(JSRuntime)); |
769 | if (!rt) |
770 | return NULL; |
771 | |
772 | /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ |
773 | memset(rt, 0, sizeof(JSRuntime)); |
774 | JS_INIT_CLIST(&rt->contextList); |
775 | JS_INIT_CLIST(&rt->trapList); |
776 | JS_INIT_CLIST(&rt->watchPointList); |
777 | |
778 | if (!js_InitDtoa()) |
779 | goto bad; |
780 | if (!js_InitGC(rt, maxbytes)) |
781 | goto bad; |
782 | if (!js_InitAtomState(rt)) |
783 | goto bad; |
784 | if (!js_InitDeflatedStringCache(rt)) |
785 | goto bad; |
786 | #ifdef JS_THREADSAFE |
787 | rt->gcLock = JS_NEW_LOCK(); |
788 | if (!rt->gcLock) |
789 | goto bad; |
790 | rt->gcDone = JS_NEW_CONDVAR(rt->gcLock); |
791 | if (!rt->gcDone) |
792 | goto bad; |
793 | rt->requestDone = JS_NEW_CONDVAR(rt->gcLock); |
794 | if (!rt->requestDone) |
795 | goto bad; |
796 | /* this is asymmetric with JS_ShutDown: */ |
797 | if (!js_SetupLocks(8, 16)) |
798 | goto bad; |
799 | rt->rtLock = JS_NEW_LOCK(); |
800 | if (!rt->rtLock) |
801 | goto bad; |
802 | rt->stateChange = JS_NEW_CONDVAR(rt->gcLock); |
803 | if (!rt->stateChange) |
804 | goto bad; |
805 | rt->titleSharingDone = JS_NEW_CONDVAR(rt->gcLock); |
806 | if (!rt->titleSharingDone) |
807 | goto bad; |
808 | rt->titleSharingTodo = NO_TITLE_SHARING_TODO; |
809 | rt->debuggerLock = JS_NEW_LOCK(); |
810 | if (!rt->debuggerLock) |
811 | goto bad; |
812 | #endif |
813 | if (!js_InitPropertyTree(rt)) |
814 | goto bad; |
815 | if (!js_InitThreads(rt)) |
816 | goto bad; |
817 | |
818 | return rt; |
819 | |
820 | bad: |
821 | JS_DestroyRuntime(rt); |
822 | return NULL; |
823 | } |
824 | |
825 | JS_PUBLIC_API(void) |
826 | JS_DestroyRuntime(JSRuntime *rt) |
827 | { |
828 | #ifdef DEBUG |
829 | /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */ |
830 | if (!JS_CLIST_IS_EMPTY(&rt->contextList)) { |
831 | JSContext *cx, *iter = NULL; |
832 | uintN cxcount = 0; |
833 | while ((cx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) { |
834 | fprintf(stderr, |
835 | "JS API usage error: found live context at %p\n", |
836 | (void *) cx); |
837 | cxcount++; |
838 | } |
839 | fprintf(stderr, |
840 | "JS API usage error: %u context%s left in runtime upon JS_DestroyRuntime.\n", |
841 | cxcount, (cxcount == 1) ? "" : "s"); |
842 | } |
843 | #endif |
844 | |
845 | js_FinishThreads(rt); |
846 | js_FreeRuntimeScriptState(rt); |
847 | js_FinishAtomState(rt); |
848 | |
849 | /* |
850 | * Free unit string storage only after all strings have been finalized, so |
851 | * that js_FinalizeString can detect unit strings and avoid calling free |
852 | * on their chars storage. |
853 | */ |
854 | js_FinishUnitStrings(rt); |
855 | |
856 | /* |
857 | * Finish the deflated string cache after the last GC and after |
858 | * calling js_FinishAtomState, which finalizes strings. |
859 | */ |
860 | js_FinishDeflatedStringCache(rt); |
861 | js_FinishGC(rt); |
862 | #ifdef JS_THREADSAFE |
863 | if (rt->gcLock) |
864 | JS_DESTROY_LOCK(rt->gcLock); |
865 | if (rt->gcDone) |
866 | JS_DESTROY_CONDVAR(rt->gcDone); |
867 | if (rt->requestDone) |
868 | JS_DESTROY_CONDVAR(rt->requestDone); |
869 | if (rt->rtLock) |
870 | JS_DESTROY_LOCK(rt->rtLock); |
871 | if (rt->stateChange) |
872 | JS_DESTROY_CONDVAR(rt->stateChange); |
873 | if (rt->titleSharingDone) |
874 | JS_DESTROY_CONDVAR(rt->titleSharingDone); |
875 | if (rt->debuggerLock) |
876 | JS_DESTROY_LOCK(rt->debuggerLock); |
877 | #endif |
878 | js_FinishPropertyTree(rt); |
879 | free(rt); |
880 | } |
881 | |
882 | JS_PUBLIC_API(void) |
883 | JS_ShutDown(void) |
884 | { |
885 | #ifdef JS_OPMETER |
886 | extern void js_DumpOpMeters(); |
887 | |
888 | js_DumpOpMeters(); |
889 | #endif |
890 | |
891 | js_FinishDtoa(); |
892 | #ifdef JS_THREADSAFE |
893 | js_CleanupLocks(); |
894 | #endif |
895 | PRMJ_NowShutdown(); |
896 | } |
897 | |
898 | JS_PUBLIC_API(void *) |
899 | JS_GetRuntimePrivate(JSRuntime *rt) |
900 | { |
901 | return rt->data; |
902 | } |
903 | |
904 | JS_PUBLIC_API(void) |
905 | JS_SetRuntimePrivate(JSRuntime *rt, void *data) |
906 | { |
907 | rt->data = data; |
908 | } |
909 | |
910 | JS_PUBLIC_API(void) |
911 | JS_BeginRequest(JSContext *cx) |
912 | { |
913 | #ifdef JS_THREADSAFE |
914 | JSRuntime *rt; |
915 | |
916 | JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); |
917 | if (!cx->requestDepth) { |
918 | JS_ASSERT(cx->gcLocalFreeLists == &js_GCEmptyFreeListSet); |
919 | |
920 | /* Wait until the GC is finished. */ |
921 | rt = cx->runtime; |
922 | JS_LOCK_GC(rt); |
923 | |
924 | if (rt->gcThread != cx->thread) { |
925 | while (rt->gcLevel > 0) |
926 | JS_AWAIT_GC_DONE(rt); |
927 | } |
928 | |
929 | /* Indicate that a request is running. */ |
930 | rt->requestCount++; |
931 | cx->requestDepth = 1; |
932 | cx->outstandingRequests++; |
933 | JS_UNLOCK_GC(rt); |
934 | return; |
935 | } |
936 | cx->requestDepth++; |
937 | cx->outstandingRequests++; |
938 | #endif |
939 | } |
940 | |
941 | JS_PUBLIC_API(void) |
942 | JS_EndRequest(JSContext *cx) |
943 | { |
944 | #ifdef JS_THREADSAFE |
945 | JSRuntime *rt; |
946 | |
947 | CHECK_REQUEST(cx); |
948 | JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread)); |
949 | JS_ASSERT(cx->requestDepth > 0); |
950 | JS_ASSERT(cx->outstandingRequests > 0); |
951 | if (cx->requestDepth == 1) { |
952 | js_LeaveTrace(cx); /* for GC safety */ |
953 | |
954 | /* Lock before clearing to interlock with ClaimScope, in jslock.c. */ |
955 | rt = cx->runtime; |
956 | JS_LOCK_GC(rt); |
957 | cx->requestDepth = 0; |
958 | cx->outstandingRequests--; |
959 | |
960 | js_ShareWaitingTitles(cx); |
961 | js_RevokeGCLocalFreeLists(cx); |
962 | |
963 | /* Give the GC a chance to run if this was the last request running. */ |
964 | JS_ASSERT(rt->requestCount > 0); |
965 | rt->requestCount--; |
966 | if (rt->requestCount == 0) |
967 | JS_NOTIFY_REQUEST_DONE(rt); |
968 | |
969 | JS_UNLOCK_GC(rt); |
970 | return; |
971 | } |
972 | |
973 | cx->requestDepth--; |
974 | cx->outstandingRequests--; |
975 | #endif |
976 | } |
977 | |
978 | /* Yield to pending GC operations, regardless of request depth */ |
979 | JS_PUBLIC_API(void) |
980 | JS_YieldRequest(JSContext *cx) |
981 | { |
982 | #ifdef JS_THREADSAFE |
983 | JS_ASSERT(cx->thread); |
984 | CHECK_REQUEST(cx); |
985 | JS_ResumeRequest(cx, JS_SuspendRequest(cx)); |
986 | #endif |
987 | } |
988 | |
989 | JS_PUBLIC_API(jsrefcount) |
990 | JS_SuspendRequest(JSContext *cx) |
991 | { |
992 | #ifdef JS_THREADSAFE |
993 | jsrefcount saveDepth = cx->requestDepth; |
994 | |
995 | while (cx->requestDepth) { |
996 | cx->outstandingRequests++; /* compensate for JS_EndRequest */ |
997 | JS_EndRequest(cx); |
998 | } |
999 | return saveDepth; |
1000 | #else |
1001 | return 0; |
1002 | #endif |
1003 | } |
1004 | |
1005 | JS_PUBLIC_API(void) |
1006 | JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth) |
1007 | { |
1008 | #ifdef JS_THREADSAFE |
1009 | JS_ASSERT(!cx->requestDepth); |
1010 | while (--saveDepth >= 0) { |
1011 | JS_BeginRequest(cx); |
1012 | cx->outstandingRequests--; /* compensate for JS_BeginRequest */ |
1013 | } |
1014 | #endif |
1015 | } |
1016 | |
1017 | JS_PUBLIC_API(void) |
1018 | JS_Lock(JSRuntime *rt) |
1019 | { |
1020 | JS_LOCK_RUNTIME(rt); |
1021 | } |
1022 | |
1023 | JS_PUBLIC_API(void) |
1024 | JS_Unlock(JSRuntime *rt) |
1025 | { |
1026 | JS_UNLOCK_RUNTIME(rt); |
1027 | } |
1028 | |
1029 | JS_PUBLIC_API(JSContextCallback) |
1030 | JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback) |
1031 | { |
1032 | JSContextCallback old; |
1033 | |
1034 | old = rt->cxCallback; |
1035 | rt->cxCallback = cxCallback; |
1036 | return old; |
1037 | } |
1038 | |
1039 | JS_PUBLIC_API(JSContext *) |
1040 | JS_NewContext(JSRuntime *rt, size_t stackChunkSize) |
1041 | { |
1042 | return js_NewContext(rt, stackChunkSize); |
1043 | } |
1044 | |
1045 | JS_PUBLIC_API(void) |
1046 | JS_DestroyContext(JSContext *cx) |
1047 | { |
1048 | js_DestroyContext(cx, JSDCM_FORCE_GC); |
1049 | } |
1050 | |
1051 | JS_PUBLIC_API(void) |
1052 | JS_DestroyContextNoGC(JSContext *cx) |
1053 | { |
1054 | js_DestroyContext(cx, JSDCM_NO_GC); |
1055 | } |
1056 | |
1057 | JS_PUBLIC_API(void) |
1058 | JS_DestroyContextMaybeGC(JSContext *cx) |
1059 | { |
1060 | js_DestroyContext(cx, JSDCM_MAYBE_GC); |
1061 | } |
1062 | |
1063 | JS_PUBLIC_API(void *) |
1064 | JS_GetContextPrivate(JSContext *cx) |
1065 | { |
1066 | return cx->data; |
1067 | } |
1068 | |
1069 | JS_PUBLIC_API(void) |
1070 | JS_SetContextPrivate(JSContext *cx, void *data) |
1071 | { |
1072 | cx->data = data; |
1073 | } |
1074 | |
1075 | JS_PUBLIC_API(JSRuntime *) |
1076 | JS_GetRuntime(JSContext *cx) |
1077 | { |
1078 | return cx->runtime; |
1079 | } |
1080 | |
1081 | JS_PUBLIC_API(JSContext *) |
1082 | JS_ContextIterator(JSRuntime *rt, JSContext **iterp) |
1083 | { |
1084 | return js_ContextIterator(rt, JS_TRUE, iterp); |
1085 | } |
1086 | |
1087 | JS_PUBLIC_API(JSVersion) |
1088 | JS_GetVersion(JSContext *cx) |
1089 | { |
1090 | return JSVERSION_NUMBER(cx); |
1091 | } |
1092 | |
1093 | JS_PUBLIC_API(JSVersion) |
1094 | JS_SetVersion(JSContext *cx, JSVersion version) |
1095 | { |
1096 | JSVersion oldVersion; |
1097 | |
1098 | JS_ASSERT(version != JSVERSION_UNKNOWN); |
1099 | JS_ASSERT((version & ~JSVERSION_MASK) == 0); |
1100 | |
1101 | oldVersion = JSVERSION_NUMBER(cx); |
1102 | if (version == oldVersion) |
1103 | return oldVersion; |
1104 | |
1105 | /* We no longer support 1.4 or below. */ |
1106 | if (version != JSVERSION_DEFAULT && version <= JSVERSION_1_4) |
1107 | return oldVersion; |
1108 | |
1109 | cx->version = (cx->version & ~JSVERSION_MASK) | version; |
1110 | js_OnVersionChange(cx); |
1111 | return oldVersion; |
1112 | } |
1113 | |
1114 | static struct v2smap { |
1115 | JSVersion version; |
1116 | const char *string; |
1117 | } v2smap[] = { |
1118 | {JSVERSION_1_0, "1.0"}, |
1119 | {JSVERSION_1_1, "1.1"}, |
1120 | {JSVERSION_1_2, "1.2"}, |
1121 | {JSVERSION_1_3, "1.3"}, |
1122 | {JSVERSION_1_4, "1.4"}, |
1123 | {JSVERSION_ECMA_3, "ECMAv3"}, |
1124 | {JSVERSION_1_5, "1.5"}, |
1125 | {JSVERSION_1_6, "1.6"}, |
1126 | {JSVERSION_1_7, "1.7"}, |
1127 | {JSVERSION_1_8, "1.8"}, |
1128 | {JSVERSION_DEFAULT, js_default_str}, |
1129 | {JSVERSION_UNKNOWN, NULL}, /* must be last, NULL is sentinel */ |
1130 | }; |
1131 | |
1132 | JS_PUBLIC_API(const char *) |
1133 | JS_VersionToString(JSVersion version) |
1134 | { |
1135 | int i; |
1136 | |
1137 | for (i = 0; v2smap[i].string; i++) |
1138 | if (v2smap[i].version == version) |
1139 | return v2smap[i].string; |
1140 | return "unknown"; |
1141 | } |
1142 | |
1143 | JS_PUBLIC_API(JSVersion) |
1144 | JS_StringToVersion(const char *string) |
1145 | { |
1146 | int i; |
1147 | |
1148 | for (i = 0; v2smap[i].string; i++) |
1149 | if (strcmp(v2smap[i].string, string) == 0) |
1150 | return v2smap[i].version; |
1151 | return JSVERSION_UNKNOWN; |
1152 | } |
1153 | |
1154 | JS_PUBLIC_API(uint32) |
1155 | JS_GetOptions(JSContext *cx) |
1156 | { |
1157 | return cx->options; |
1158 | } |
1159 | |
1160 | JS_PUBLIC_API(uint32) |
1161 | JS_SetOptions(JSContext *cx, uint32 options) |
1162 | { |
1163 | uint32 oldopts = cx->options; |
1164 | cx->options = options; |
1165 | js_SyncOptionsToVersion(cx); |
1166 | return oldopts; |
1167 | } |
1168 | |
1169 | JS_PUBLIC_API(uint32) |
1170 | JS_ToggleOptions(JSContext *cx, uint32 options) |
1171 | { |
1172 | uint32 oldopts = cx->options; |
1173 | cx->options ^= options; |
1174 | js_SyncOptionsToVersion(cx); |
1175 | return oldopts; |
1176 | } |
1177 | |
1178 | JS_PUBLIC_API(const char *) |
1179 | JS_GetImplementationVersion(void) |
1180 | { |
1181 | return "JavaScript-C 1.8.0 pre-release 1 2007-10-03"; |
1182 | } |
1183 | |
1184 | |
1185 | JS_PUBLIC_API(JSObject *) |
1186 | JS_GetGlobalObject(JSContext *cx) |
1187 | { |
1188 | return cx->globalObject; |
1189 | } |
1190 | |
1191 | JS_PUBLIC_API(void) |
1192 | JS_SetGlobalObject(JSContext *cx, JSObject *obj) |
1193 | { |
1194 | cx->globalObject = obj; |
1195 | |
1196 | #if JS_HAS_XML_SUPPORT |
1197 | cx->xmlSettingFlags = 0; |
1198 | #endif |
1199 | } |
1200 | |
1201 | JS_BEGIN_EXTERN_C |
1202 | |
1203 | JSObject * |
1204 | js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) |
1205 | { |
1206 | JSDHashTable *table; |
1207 | JSBool resolving; |
1208 | JSRuntime *rt; |
1209 | JSResolvingKey key; |
1210 | JSResolvingEntry *entry; |
1211 | JSObject *fun_proto, *obj_proto; |
1212 | |
1213 | /* If cx has no global object, use obj so prototypes can be found. */ |
1214 | if (!cx->globalObject) |
1215 | JS_SetGlobalObject(cx, obj); |
1216 | |
1217 | /* Record Function and Object in cx->resolvingTable, if we are resolving. */ |
1218 | table = cx->resolvingTable; |
1219 | resolving = (table && table->entryCount); |
1220 | rt = cx->runtime; |
1221 | key.obj = obj; |
1222 | if (resolving) { |
1223 | key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]); |
1224 | entry = (JSResolvingEntry *) |
1225 | JS_DHashTableOperate(table, &key, JS_DHASH_ADD); |
1226 | if (entry && entry->key.obj && (entry->flags & JSRESFLAG_LOOKUP)) { |
1227 | /* Already resolving Function, record Object too. */ |
1228 | JS_ASSERT(entry->key.obj == obj); |
1229 | key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); |
1230 | entry = (JSResolvingEntry *) |
1231 | JS_DHashTableOperate(table, &key, JS_DHASH_ADD); |
1232 | } |
1233 | if (!entry) { |
1234 | JS_ReportOutOfMemory(cx); |
1235 | return NULL; |
1236 | } |
1237 | JS_ASSERT(!entry->key.obj && entry->flags == 0); |
1238 | entry->key = key; |
1239 | entry->flags = JSRESFLAG_LOOKUP; |
1240 | } else { |
1241 | key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); |
1242 | if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) |
1243 | return NULL; |
1244 | |
1245 | key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]); |
1246 | if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) { |
1247 | key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); |
1248 | JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); |
1249 | return NULL; |
1250 | } |
1251 | |
1252 | table = cx->resolvingTable; |
1253 | } |
1254 | |
1255 | /* Initialize the function class first so constructors can be made. */ |
1256 | if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Function), |
1257 | &fun_proto)) { |
1258 | fun_proto = NULL; |
1259 | goto out; |
1260 | } |
1261 | if (!fun_proto) { |
1262 | fun_proto = js_InitFunctionClass(cx, obj); |
1263 | if (!fun_proto) |
1264 | goto out; |
1265 | } else { |
1266 | JSObject *ctor; |
1267 | |
1268 | ctor = JS_GetConstructor(cx, fun_proto); |
1269 | if (!ctor) { |
1270 | fun_proto = NULL; |
1271 | goto out; |
1272 | } |
1273 | OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(CLASS_ATOM(cx, Function)), |
1274 | OBJECT_TO_JSVAL(ctor), 0, 0, 0, NULL); |
1275 | } |
1276 | |
1277 | /* Initialize the object class next so Object.prototype works. */ |
1278 | if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object), |
1279 | &obj_proto)) { |
1280 | fun_proto = NULL; |
1281 | goto out; |
1282 | } |
1283 | if (!obj_proto) |
1284 | obj_proto = js_InitObjectClass(cx, obj); |
1285 | if (!obj_proto) { |
1286 | fun_proto = NULL; |
1287 | goto out; |
1288 | } |
1289 | |
1290 | /* Function.prototype and the global object delegate to Object.prototype. */ |
1291 | OBJ_SET_PROTO(cx, fun_proto, obj_proto); |
1292 | if (!OBJ_GET_PROTO(cx, obj)) |
1293 | OBJ_SET_PROTO(cx, obj, obj_proto); |
1294 | |
1295 | out: |
1296 | /* If resolving, remove the other entry (Object or Function) from table. */ |
1297 | JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); |
1298 | if (!resolving) { |
1299 | /* If not resolving, remove the first entry added above, for Object. */ |
1300 | JS_ASSERT(key.id == \ |
1301 | ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function])); |
1302 | key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); |
1303 | JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); |
1304 | } |
1305 | return fun_proto; |
1306 | } |
1307 | |
1308 | JS_END_EXTERN_C |
1309 | |
1310 | JS_PUBLIC_API(JSBool) |
1311 | JS_InitStandardClasses(JSContext *cx, JSObject *obj) |
1312 | { |
1313 | JSAtom *atom; |
1314 | |
1315 | CHECK_REQUEST(cx); |
1316 | |
1317 | /* Define a top-level property 'undefined' with the undefined value. */ |
1318 | atom = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; |
1319 | if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, |
1320 | JS_PropertyStub, JS_PropertyStub, JSPROP_PERMANENT, |
1321 | NULL)) { |
1322 | return JS_FALSE; |
1323 | } |
1324 | |
1325 | /* Function and Object require cooperative bootstrapping magic. */ |
1326 | if (!js_InitFunctionAndObjectClasses(cx, obj)) |
1327 | return JS_FALSE; |
1328 | |
1329 | /* Initialize the rest of the standard objects and functions. */ |
1330 | return js_InitArrayClass(cx, obj) && |
1331 | js_InitBooleanClass(cx, obj) && |
1332 | js_InitExceptionClasses(cx, obj) && |
1333 | js_InitMathClass(cx, obj) && |
1334 | js_InitNumberClass(cx, obj) && |
1335 | js_InitJSONClass(cx, obj) && |
1336 | js_InitRegExpClass(cx, obj) && |
1337 | js_InitStringClass(cx, obj) && |
1338 | js_InitEval(cx, obj) && |
1339 | #if JS_HAS_SCRIPT_OBJECT |
1340 | js_InitScriptClass(cx, obj) && |
1341 | #endif |
1342 | #if JS_HAS_XML_SUPPORT |
1343 | js_InitXMLClasses(cx, obj) && |
1344 | #endif |
1345 | #if JS_HAS_FILE_OBJECT |
1346 | js_InitFileClass(cx, obj) && |
1347 | #endif |
1348 | #if JS_HAS_GENERATORS |
1349 | js_InitIteratorClasses(cx, obj) && |
1350 | #endif |
1351 | js_InitDateClass(cx, obj); |
1352 | } |
1353 | |
1354 | #define CLASP(name) (&js_##name##Class) |
1355 | #define XCLASP(name) (&js_##name##Class.base) |
1356 | #define EAGER_ATOM(name) ATOM_OFFSET(name), NULL |
1357 | #define EAGER_CLASS_ATOM(name) CLASS_ATOM_OFFSET(name), NULL |
1358 | #define EAGER_ATOM_AND_CLASP(name) EAGER_CLASS_ATOM(name), CLASP(name) |
1359 | #define EAGER_ATOM_AND_XCLASP(name) EAGER_CLASS_ATOM(name), XCLASP(name) |
1360 | #define LAZY_ATOM(name) ATOM_OFFSET(lazy.name), js_##name##_str |
1361 | |
1362 | typedef struct JSStdName { |
1363 | JSObjectOp init; |
1364 | size_t atomOffset; /* offset of atom pointer in JSAtomState */ |
1365 | const char *name; /* null if atom is pre-pinned, else name */ |
1366 | JSClass *clasp; |
1367 | } JSStdName; |
1368 | |
1369 | static JSAtom * |
1370 | StdNameToAtom(JSContext *cx, JSStdName *stdn) |
1371 | { |
1372 | size_t offset; |
1373 | JSAtom *atom; |
1374 | const char *name; |
1375 | |
1376 | offset = stdn->atomOffset; |
1377 | atom = OFFSET_TO_ATOM(cx->runtime, offset); |
1378 | if (!atom) { |
1379 | name = stdn->name; |
1380 | if (name) { |
1381 | atom = js_Atomize(cx, name, strlen(name), ATOM_PINNED); |
1382 | OFFSET_TO_ATOM(cx->runtime, offset) = atom; |
1383 | } |
1384 | } |
1385 | return atom; |
1386 | } |
1387 | |
1388 | /* |
1389 | * Table of class initializers and their atom offsets in rt->atomState. |
1390 | * If you add a "standard" class, remember to update this table. |
1391 | */ |
1392 | static JSStdName standard_class_atoms[] = { |
1393 | {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Function)}, |
1394 | {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Object)}, |
1395 | {js_InitArrayClass, EAGER_ATOM_AND_CLASP(Array)}, |
1396 | {js_InitBooleanClass, EAGER_ATOM_AND_CLASP(Boolean)}, |
1397 | {js_InitDateClass, EAGER_ATOM_AND_CLASP(Date)}, |
1398 | {js_InitMathClass, EAGER_ATOM_AND_CLASP(Math)}, |
1399 | {js_InitNumberClass, EAGER_ATOM_AND_CLASP(Number)}, |
1400 | {js_InitStringClass, EAGER_ATOM_AND_CLASP(String)}, |
1401 | {js_InitExceptionClasses, EAGER_ATOM_AND_CLASP(Error)}, |
1402 | {js_InitRegExpClass, EAGER_ATOM_AND_CLASP(RegExp)}, |
1403 | #if JS_HAS_SCRIPT_OBJECT |
1404 | {js_InitScriptClass, EAGER_ATOM_AND_CLASP(Script)}, |
1405 | #endif |
1406 | #if JS_HAS_XML_SUPPORT |
1407 | {js_InitXMLClass, EAGER_ATOM_AND_CLASP(XML)}, |
1408 | {js_InitNamespaceClass, EAGER_ATOM_AND_XCLASP(Namespace)}, |
1409 | {js_InitQNameClass, EAGER_ATOM_AND_XCLASP(QName)}, |
1410 | #endif |
1411 | #if JS_HAS_FILE_OBJECT |
1412 | {js_InitFileClass, EAGER_ATOM_AND_CLASP(File)}, |
1413 | #endif |
1414 | #if JS_HAS_GENERATORS |
1415 | {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(StopIteration)}, |
1416 | #endif |
1417 | {js_InitJSONClass, EAGER_ATOM_AND_CLASP(JSON)}, |
1418 | {NULL, 0, NULL, NULL} |
1419 | }; |
1420 | |
1421 | /* |
1422 | * Table of top-level function and constant names and their init functions. |
1423 | * If you add a "standard" global function or property, remember to update |
1424 | * this table. |
1425 | */ |
1426 | static JSStdName standard_class_names[] = { |
1427 | /* ECMA requires that eval be a direct property of the global object. */ |
1428 | {js_InitEval, EAGER_ATOM(eval), NULL}, |
1429 | |
1430 | /* Global properties and functions defined by the Number class. */ |
1431 | {js_InitNumberClass, LAZY_ATOM(NaN), NULL}, |
1432 | {js_InitNumberClass, LAZY_ATOM(Infinity), NULL}, |
1433 | {js_InitNumberClass, LAZY_ATOM(isNaN), NULL}, |
1434 | {js_InitNumberClass, LAZY_ATOM(isFinite), NULL}, |
1435 | {js_InitNumberClass, LAZY_ATOM(parseFloat), NULL}, |
1436 | {js_InitNumberClass, LAZY_ATOM(parseInt), NULL}, |
1437 | |
1438 | /* String global functions. */ |
1439 | {js_InitStringClass, LAZY_ATOM(escape), NULL}, |
1440 | {js_InitStringClass, LAZY_ATOM(unescape), NULL}, |
1441 | {js_InitStringClass, LAZY_ATOM(decodeURI), NULL}, |
1442 | {js_InitStringClass, LAZY_ATOM(encodeURI), NULL}, |
1443 | {js_InitStringClass, LAZY_ATOM(decodeURIComponent), NULL}, |
1444 | {js_InitStringClass, LAZY_ATOM(encodeURIComponent), NULL}, |
1445 | #if JS_HAS_UNEVAL |
1446 | {js_InitStringClass, LAZY_ATOM(uneval), NULL}, |
1447 | #endif |
1448 | |
1449 | /* Exception constructors. */ |
1450 | {js_InitExceptionClasses, EAGER_CLASS_ATOM(Error), CLASP(Error)}, |
1451 | {js_InitExceptionClasses, EAGER_CLASS_ATOM(InternalError), CLASP(Error)}, |
1452 | {js_InitExceptionClasses, EAGER_CLASS_ATOM(EvalError), CLASP(Error)}, |
1453 | {js_InitExceptionClasses, EAGER_CLASS_ATOM(RangeError), CLASP(Error)}, |
1454 | {js_InitExceptionClasses, EAGER_CLASS_ATOM(ReferenceError), CLASP(Error)}, |
1455 | {js_InitExceptionClasses, EAGER_CLASS_ATOM(SyntaxError), CLASP(Error)}, |
1456 | {js_InitExceptionClasses, EAGER_CLASS_ATOM(TypeError), CLASP(Error)}, |
1457 | {js_InitExceptionClasses, EAGER_CLASS_ATOM(URIError), CLASP(Error)}, |
1458 | |
1459 | #if JS_HAS_XML_SUPPORT |
1460 | {js_InitAnyNameClass, EAGER_ATOM_AND_CLASP(AnyName)}, |
1461 | {js_InitAttributeNameClass, EAGER_ATOM_AND_CLASP(AttributeName)}, |
1462 | {js_InitXMLClass, LAZY_ATOM(XMLList), &js_XMLClass}, |
1463 | {js_InitXMLClass, LAZY_ATOM(isXMLName), NULL}, |
1464 | #endif |
1465 | |
1466 | #if JS_HAS_GENERATORS |
1467 | {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Iterator)}, |
1468 | {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Generator)}, |
1469 | #endif |
1470 | |
1471 | {NULL, 0, NULL, NULL} |
1472 | }; |
1473 | |
1474 | static JSStdName object_prototype_names[] = { |
1475 | /* Object.prototype properties (global delegates to Object.prototype). */ |
1476 | {js_InitObjectClass, EAGER_ATOM(proto), NULL}, |
1477 | {js_InitObjectClass, EAGER_ATOM(parent), NULL}, |
1478 | {js_InitObjectClass, EAGER_ATOM(count), NULL}, |
1479 | #if JS_HAS_TOSOURCE |
1480 | {js_InitObjectClass, EAGER_ATOM(toSource), NULL}, |
1481 | #endif |
1482 | {js_InitObjectClass, EAGER_ATOM(toString), NULL}, |
1483 | {js_InitObjectClass, EAGER_ATOM(toLocaleString), NULL}, |
1484 | {js_InitObjectClass, EAGER_ATOM(valueOf), NULL}, |
1485 | #if JS_HAS_OBJ_WATCHPOINT |
1486 | {js_InitObjectClass, LAZY_ATOM(watch), NULL}, |
1487 | {js_InitObjectClass, LAZY_ATOM(unwatch), NULL}, |
1488 | #endif |
1489 | {js_InitObjectClass, LAZY_ATOM(hasOwnProperty), NULL}, |
1490 | {js_InitObjectClass, LAZY_ATOM(isPrototypeOf), NULL}, |
1491 | {js_InitObjectClass, LAZY_ATOM(propertyIsEnumerable), NULL}, |
1492 | #if JS_HAS_GETTER_SETTER |
1493 | {js_InitObjectClass, LAZY_ATOM(defineGetter), NULL}, |
1494 | {js_InitObjectClass, LAZY_ATOM(defineSetter), NULL}, |
1495 | {js_InitObjectClass, LAZY_ATOM(lookupGetter), NULL}, |
1496 | {js_InitObjectClass, LAZY_ATOM(lookupSetter), NULL}, |
1497 | #endif |
1498 | |
1499 | {NULL, 0, NULL, NULL} |
1500 | }; |
1501 | |
1502 | JS_PUBLIC_API(JSBool) |
1503 | JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, |
1504 | JSBool *resolved) |
1505 | { |
1506 | JSString *idstr; |
1507 | JSRuntime *rt; |
1508 | JSAtom *atom; |
1509 | JSStdName *stdnm; |
1510 | uintN i; |
1511 | |
1512 | CHECK_REQUEST(cx); |
1513 | *resolved = JS_FALSE; |
1514 | |
1515 | rt = cx->runtime; |
1516 | JS_ASSERT(rt->state != JSRTS_DOWN); |
1517 | if (rt->state == JSRTS_LANDING || !JSVAL_IS_STRING(id)) |
1518 | return JS_TRUE; |
1519 | |
1520 | idstr = JSVAL_TO_STRING(id); |
1521 | |
1522 | /* Check whether we're resolving 'undefined', and define it if so. */ |
1523 | atom = rt->atomState.typeAtoms[JSTYPE_VOID]; |
1524 | if (idstr == ATOM_TO_STRING(atom)) { |
1525 | *resolved = JS_TRUE; |
1526 | return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, |
1527 | JS_PropertyStub, JS_PropertyStub, |
1528 | JSPROP_PERMANENT, NULL); |
1529 | } |
1530 | |
1531 | /* Try for class constructors/prototypes named by well-known atoms. */ |
1532 | stdnm = NULL; |
1533 | for (i = 0; standard_class_atoms[i].init; i++) { |
1534 | atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset); |
1535 | if (idstr == ATOM_TO_STRING(atom)) { |
1536 | stdnm = &standard_class_atoms[i]; |
1537 | break; |
1538 | } |
1539 | } |
1540 | |
1541 | if (!stdnm) { |
1542 | /* Try less frequently used top-level functions and constants. */ |
1543 | for (i = 0; standard_class_names[i].init; i++) { |
1544 | atom = StdNameToAtom(cx, &standard_class_names[i]); |
1545 | if (!atom) |
1546 | return JS_FALSE; |
1547 | if (idstr == ATOM_TO_STRING(atom)) { |
1548 | stdnm = &standard_class_names[i]; |
1549 | break; |
1550 | } |
1551 | } |
1552 | |
1553 | if (!stdnm && !OBJ_GET_PROTO(cx, obj)) { |
1554 | /* |
1555 | * Try even less frequently used names delegated from the global |
1556 | * object to Object.prototype, but only if the Object class hasn't |
1557 | * yet been initialized. |
1558 | */ |
1559 | for (i = 0; object_prototype_names[i].init; i++) { |
1560 | atom = StdNameToAtom(cx, &object_prototype_names[i]); |
1561 | if (!atom) |
1562 | return JS_FALSE; |
1563 | if (idstr == ATOM_TO_STRING(atom)) { |
1564 | stdnm = &standard_class_names[i]; |
1565 | break; |
1566 | } |
1567 | } |
1568 | } |
1569 | } |
1570 | |
1571 | if (stdnm) { |
1572 | /* |
1573 | * If this standard class is anonymous and obj advertises itself as a |
1574 | * global object (in order to reserve slots for standard class object |
1575 | * pointers), then we don't want to resolve by name. |
1576 | * |
1577 | * If inversely, either id does not name a class, or id does not name |
1578 | * an anonymous class, or the global does not reserve slots for class |
1579 | * objects, then we must call the init hook here. |
1580 | */ |
1581 | if (stdnm->clasp && |
1582 | (stdnm->clasp->flags & JSCLASS_IS_ANONYMOUS) && |
1583 | (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) { |
1584 | return JS_TRUE; |
1585 | } |
1586 | |
1587 | if (!stdnm->init(cx, obj)) |
1588 | return JS_FALSE; |
1589 | *resolved = JS_TRUE; |
1590 | } |
1591 | return JS_TRUE; |
1592 | } |
1593 | |
1594 | static JSBool |
1595 | AlreadyHasOwnProperty(JSContext *cx, JSObject *obj, JSAtom *atom) |
1596 | { |
1597 | JSScopeProperty *sprop; |
1598 | JSScope *scope; |
1599 | |
1600 | JS_ASSERT(OBJ_IS_NATIVE(obj)); |
1601 | JS_LOCK_OBJ(cx, obj); |
1602 | scope = OBJ_SCOPE(obj); |
1603 | sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); |
1604 | JS_UNLOCK_SCOPE(cx, scope); |
1605 | return sprop != NULL; |
1606 | } |
1607 | |
1608 | JS_PUBLIC_API(JSBool) |
1609 | JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj) |
1610 | { |
1611 | JSRuntime *rt; |
1612 | JSAtom *atom; |
1613 | uintN i; |
1614 | |
1615 | CHECK_REQUEST(cx); |
1616 | rt = cx->runtime; |
1617 | |
1618 | /* Check whether we need to bind 'undefined' and define it if so. */ |
1619 | atom = rt->atomState.typeAtoms[JSTYPE_VOID]; |
1620 | if (!AlreadyHasOwnProperty(cx, obj, atom) && |
1621 | !OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, |
1622 | JS_PropertyStub, JS_PropertyStub, JSPROP_PERMANENT, |
1623 | NULL)) { |
1624 | return JS_FALSE; |
1625 | } |
1626 | |
1627 | /* Initialize any classes that have not been resolved yet. */ |
1628 | for (i = 0; standard_class_atoms[i].init; i++) { |
1629 | atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset); |
1630 | if (!AlreadyHasOwnProperty(cx, obj, atom) && |
1631 | !standard_class_atoms[i].init(cx, obj)) { |
1632 | return JS_FALSE; |
1633 | } |
1634 | } |
1635 | |
1636 | return JS_TRUE; |
1637 | } |
1638 | |
1639 | static JSIdArray * |
1640 | NewIdArray(JSContext *cx, jsint length) |
1641 | { |
1642 | JSIdArray *ida; |
1643 | |
1644 | ida = (JSIdArray *) |
1645 | JS_malloc(cx, offsetof(JSIdArray, vector) + length * sizeof(jsval)); |
1646 | if (ida) |
1647 | ida->length = length; |
1648 | return ida; |
1649 | } |
1650 | |
1651 | /* |
1652 | * Unlike realloc(3), this function frees ida on failure. |
1653 | */ |
1654 | static JSIdArray * |
1655 | SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length) |
1656 | { |
1657 | JSIdArray *rida; |
1658 | |
1659 | rida = (JSIdArray *) |
1660 | JS_realloc(cx, ida, |
1661 | offsetof(JSIdArray, vector) + length * sizeof(jsval)); |
1662 | if (!rida) |
1663 | JS_DestroyIdArray(cx, ida); |
1664 | else |
1665 | rida->length = length; |
1666 | return rida; |
1667 | } |
1668 | |
1669 | static JSIdArray * |
1670 | AddAtomToArray(JSContext *cx, JSAtom *atom, JSIdArray *ida, jsint *ip) |
1671 | { |
1672 | jsint i, length; |
1673 | |
1674 | i = *ip; |
1675 | length = ida->length; |
1676 | if (i >= length) { |
1677 | ida = SetIdArrayLength(cx, ida, JS_MAX(length * 2, 8)); |
1678 | if (!ida) |
1679 | return NULL; |
1680 | JS_ASSERT(i < ida->length); |
1681 | } |
1682 | ida->vector[i] = ATOM_TO_JSID(atom); |
1683 | *ip = i + 1; |
1684 | return ida; |
1685 | } |
1686 | |
1687 | static JSIdArray * |
1688 | EnumerateIfResolved(JSContext *cx, JSObject *obj, JSAtom *atom, JSIdArray *ida, |
1689 | jsint *ip, JSBool *foundp) |
1690 | { |
1691 | *foundp = AlreadyHasOwnProperty(cx, obj, atom); |
1692 | if (*foundp) |
1693 | ida = AddAtomToArray(cx, atom, ida, ip); |
1694 | return ida; |
1695 | } |
1696 | |
1697 | JS_PUBLIC_API(JSIdArray *) |
1698 | JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, |
1699 | JSIdArray *ida) |
1700 | { |
1701 | JSRuntime *rt; |
1702 | jsint i, j, k; |
1703 | JSAtom *atom; |
1704 | JSBool found; |
1705 | JSObjectOp init; |
1706 | |
1707 | CHECK_REQUEST(cx); |
1708 | rt = cx->runtime; |
1709 | if (ida) { |
1710 | i = ida->length; |
1711 | } else { |
1712 | ida = NewIdArray(cx, 8); |
1713 | if (!ida) |
1714 | return NULL; |
1715 | i = 0; |
1716 | } |
1717 | |
1718 | /* Check whether 'undefined' has been resolved and enumerate it if so. */ |
1719 | atom = rt->atomState.typeAtoms[JSTYPE_VOID]; |
1720 | ida = EnumerateIfResolved(cx, obj, atom, ida, &i, &found); |
1721 | if (!ida) |
1722 | return NULL; |
1723 | |
1724 | /* Enumerate only classes that *have* been resolved. */ |
1725 | for (j = 0; standard_class_atoms[j].init; j++) { |
1726 | atom = OFFSET_TO_ATOM(rt, standard_class_atoms[j].atomOffset); |
1727 | ida = EnumerateIfResolved(cx, obj, atom, ida, &i, &found); |
1728 | if (!ida) |
1729 | return NULL; |
1730 | |
1731 | if (found) { |
1732 | init = standard_class_atoms[j].init; |
1733 | |
1734 | for (k = 0; standard_class_names[k].init; k++) { |
1735 | if (standard_class_names[k].init == init) { |
1736 | atom = StdNameToAtom(cx, &standard_class_names[k]); |
1737 | ida = AddAtomToArray(cx, atom, ida, &i); |
1738 | if (!ida) |
1739 | return NULL; |
1740 | } |
1741 | } |
1742 | |
1743 | if (init == js_InitObjectClass) { |
1744 | for (k = 0; object_prototype_names[k].init; k++) { |
1745 | atom = StdNameToAtom(cx, &object_prototype_names[k]); |
1746 | ida = AddAtomToArray(cx, atom, ida, &i); |
1747 | if (!ida) |
1748 | return NULL; |
1749 | } |
1750 | } |
1751 | } |
1752 | } |
1753 | |
1754 | /* Trim to exact length. */ |
1755 | return SetIdArrayLength(cx, ida, i); |
1756 | } |
1757 | |
1758 | #undef CLASP |
1759 | #undef EAGER_ATOM |
1760 | #undef EAGER_CLASS_ATOM |
1761 | #undef EAGER_ATOM_CLASP |
1762 | #undef LAZY_ATOM |
1763 | |
1764 | JS_PUBLIC_API(JSBool) |
1765 | JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, |
1766 | JSObject **objp) |
1767 | { |
1768 | CHECK_REQUEST(cx); |
1769 | return js_GetClassObject(cx, obj, key, objp); |
1770 | } |
1771 | |
1772 | JS_PUBLIC_API(JSObject *) |
1773 | JS_GetScopeChain(JSContext *cx) |
1774 | { |
1775 | JSStackFrame *fp; |
1776 | |
1777 | CHECK_REQUEST(cx); |
1778 | fp = js_GetTopStackFrame(cx); |
1779 | if (!fp) { |
1780 | /* |
1781 | * There is no code active on this context. In place of an actual |
1782 | * scope chain, use the context's global object, which is set in |
1783 | * js_InitFunctionAndObjectClasses, and which represents the default |
1784 | * scope chain for the embedding. See also js_FindClassObject. |
1785 | * |
1786 | * For embeddings that use the inner and outer object hooks, the inner |
1787 | * object represents the ultimate global object, with the outer object |
1788 | * acting as a stand-in. |
1789 | */ |
1790 | JSObject *obj = cx->globalObject; |
1791 | if (!obj) { |
1792 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); |
1793 | return NULL; |
1794 | } |
1795 | |
1796 | OBJ_TO_INNER_OBJECT(cx, obj); |
1797 | return obj; |
1798 | } |
1799 | return js_GetScopeChain(cx, fp); |
1800 | } |
1801 | |
1802 | JS_PUBLIC_API(JSObject *) |
1803 | JS_GetGlobalForObject(JSContext *cx, JSObject *obj) |
1804 | { |
1805 | JSObject *parent; |
1806 | |
1807 | while ((parent = OBJ_GET_PARENT(cx, obj)) != NULL) |
1808 | obj = parent; |
1809 | return obj; |
1810 | } |
1811 | |
1812 | JS_PUBLIC_API(jsval) |
1813 | JS_ComputeThis(JSContext *cx, jsval *vp) |
1814 | { |
1815 | if (!js_ComputeThis(cx, JS_FALSE, vp + 2)) |
1816 | return JSVAL_NULL; |
1817 | return vp[1]; |
1818 | } |
1819 | |
1820 | JS_PUBLIC_API(void *) |
1821 | JS_malloc(JSContext *cx, size_t nbytes) |
1822 | { |
1823 | void *p; |
1824 | |
1825 | JS_ASSERT(nbytes != 0); |
1826 | if (nbytes == 0) |
1827 | nbytes = 1; |
1828 | |
1829 | p = malloc(nbytes); |
1830 | if (!p) { |
1831 | JS_ReportOutOfMemory(cx); |
1832 | return NULL; |
1833 | } |
1834 | js_UpdateMallocCounter(cx, nbytes); |
1835 | |
1836 | return p; |
1837 | } |
1838 | |
1839 | JS_PUBLIC_API(void *) |
1840 | JS_realloc(JSContext *cx, void *p, size_t nbytes) |
1841 | { |
1842 | void *orig = p; |
1843 | p = realloc(p, nbytes); |
1844 | if (!p) { |
1845 | JS_ReportOutOfMemory(cx); |
1846 | return NULL; |
1847 | } |
1848 | if (!orig) |
1849 | js_UpdateMallocCounter(cx, nbytes); |
1850 | return p; |
1851 | } |
1852 | |
1853 | JS_PUBLIC_API(void) |
1854 | JS_free(JSContext *cx, void *p) |
1855 | { |
1856 | if (p) |
1857 | free(p); |
1858 | } |
1859 | |
1860 | JS_PUBLIC_API(char *) |
1861 | JS_strdup(JSContext *cx, const char *s) |
1862 | { |
1863 | size_t n; |
1864 | void *p; |
1865 | |
1866 | n = strlen(s) + 1; |
1867 | p = JS_malloc(cx, n); |
1868 | if (!p) |
1869 | return NULL; |
1870 | return (char *)memcpy(p, s, n); |
1871 | } |
1872 | |
1873 | JS_PUBLIC_API(jsdouble *) |
1874 | JS_NewDouble(JSContext *cx, jsdouble d) |
1875 | { |
1876 | CHECK_REQUEST(cx); |
1877 | return js_NewWeaklyRootedDouble(cx, d); |
1878 | } |
1879 | |
1880 | JS_PUBLIC_API(JSBool) |
1881 | JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval) |
1882 | { |
1883 | jsdouble *dp; |
1884 | |
1885 | CHECK_REQUEST(cx); |
1886 | dp = js_NewWeaklyRootedDouble(cx, d); |
1887 | if (!dp) |
1888 | return JS_FALSE; |
1889 | *rval = DOUBLE_TO_JSVAL(dp); |
1890 | return JS_TRUE; |
1891 | } |
1892 | |
1893 | JS_PUBLIC_API(JSBool) |
1894 | JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) |
1895 | { |
1896 | CHECK_REQUEST(cx); |
1897 | return js_NewWeaklyRootedNumber(cx, d, rval); |
1898 | } |
1899 | |
1900 | #undef JS_AddRoot |
1901 | JS_PUBLIC_API(JSBool) |
1902 | JS_AddRoot(JSContext *cx, void *rp) |
1903 | { |
1904 | CHECK_REQUEST(cx); |
1905 | return js_AddRoot(cx, rp, NULL); |
1906 | } |
1907 | |
1908 | JS_PUBLIC_API(JSBool) |
1909 | JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name) |
1910 | { |
1911 | return js_AddRootRT(rt, rp, name); |
1912 | } |
1913 | |
1914 | JS_PUBLIC_API(JSBool) |
1915 | JS_RemoveRoot(JSContext *cx, void *rp) |
1916 | { |
1917 | CHECK_REQUEST(cx); |
1918 | return js_RemoveRoot(cx->runtime, rp); |
1919 | } |
1920 | |
1921 | JS_PUBLIC_API(JSBool) |
1922 | JS_RemoveRootRT(JSRuntime *rt, void *rp) |
1923 | { |
1924 | return js_RemoveRoot(rt, rp); |
1925 | } |
1926 | |
1927 | JS_PUBLIC_API(JSBool) |
1928 | JS_AddNamedRoot(JSContext *cx, void *rp, const char *name) |
1929 | { |
1930 | CHECK_REQUEST(cx); |
1931 | return js_AddRoot(cx, rp, name); |
1932 | } |
1933 | |
1934 | JS_PUBLIC_API(void) |
1935 | JS_ClearNewbornRoots(JSContext *cx) |
1936 | { |
1937 | JS_CLEAR_WEAK_ROOTS(&cx->weakRoots); |
1938 | } |
1939 | |
1940 | JS_PUBLIC_API(JSBool) |
1941 | JS_EnterLocalRootScope(JSContext *cx) |
1942 | { |
1943 | CHECK_REQUEST(cx); |
1944 | return js_EnterLocalRootScope(cx); |
1945 | } |
1946 | |
1947 | JS_PUBLIC_API(void) |
1948 | JS_LeaveLocalRootScope(JSContext *cx) |
1949 | { |
1950 | CHECK_REQUEST(cx); |
1951 | js_LeaveLocalRootScope(cx); |
1952 | } |
1953 | |
1954 | JS_PUBLIC_API(void) |
1955 | JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) |
1956 | { |
1957 | CHECK_REQUEST(cx); |
1958 | js_LeaveLocalRootScopeWithResult(cx, rval); |
1959 | } |
1960 | |
1961 | JS_PUBLIC_API(void) |
1962 | JS_ForgetLocalRoot(JSContext *cx, void *thing) |
1963 | { |
1964 | CHECK_REQUEST(cx); |
1965 | js_ForgetLocalRoot(cx, (jsval) thing); |
1966 | } |
1967 | |
1968 | #ifdef DEBUG |
1969 | |
1970 | JS_PUBLIC_API(void) |
1971 | JS_DumpNamedRoots(JSRuntime *rt, |
1972 | void (*dump)(const char *name, void *rp, void *data), |
1973 | void *data) |
1974 | { |
1975 | js_DumpNamedRoots(rt, dump, data); |
1976 | } |
1977 | |
1978 | #endif /* DEBUG */ |
1979 | |
1980 | JS_PUBLIC_API(uint32) |
1981 | JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) |
1982 | { |
1983 | return js_MapGCRoots(rt, map, data); |
1984 | } |
1985 | |
1986 | JS_PUBLIC_API(JSBool) |
1987 | JS_LockGCThing(JSContext *cx, void *thing) |
1988 | { |
1989 | JSBool ok; |
1990 | |
1991 | CHECK_REQUEST(cx); |
1992 | ok = js_LockGCThingRT(cx->runtime, thing); |
1993 | if (!ok) |
1994 | JS_ReportOutOfMemory(cx); |
1995 | return ok; |
1996 | } |
1997 | |
1998 | JS_PUBLIC_API(JSBool) |
1999 | JS_LockGCThingRT(JSRuntime *rt, void *thing) |
2000 | { |
2001 | return js_LockGCThingRT(rt, thing); |
2002 | } |
2003 | |
2004 | JS_PUBLIC_API(JSBool) |
2005 | JS_UnlockGCThing(JSContext *cx, void *thing) |
2006 | { |
2007 | JSBool ok; |
2008 | |
2009 | CHECK_REQUEST(cx); |
2010 | ok = js_UnlockGCThingRT(cx->runtime, thing); |
2011 | if (!ok) |
2012 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_UNLOCK); |
2013 | return ok; |
2014 | } |
2015 | |
2016 | JS_PUBLIC_API(JSBool) |
2017 | JS_UnlockGCThingRT(JSRuntime *rt, void *thing) |
2018 | { |
2019 | return js_UnlockGCThingRT(rt, thing); |
2020 | } |
2021 | |
2022 | JS_PUBLIC_API(void) |
2023 | JS_SetExtraGCRoots(JSRuntime *rt, JSTraceDataOp traceOp, void *data) |
2024 | { |
2025 | rt->gcExtraRootsTraceOp = traceOp; |
2026 | rt->gcExtraRootsData = data; |
2027 | } |
2028 | |
2029 | JS_PUBLIC_API(void) |
2030 | JS_TraceRuntime(JSTracer *trc) |
2031 | { |
2032 | JSBool allAtoms = trc->context->runtime->gcKeepAtoms != 0; |
2033 | |
2034 | js_LeaveTrace(trc->context); |
2035 | js_TraceRuntime(trc, allAtoms); |
2036 | } |
2037 | |
2038 | #ifdef DEBUG |
2039 | |
2040 | #ifdef HAVE_XPCONNECT |
2041 | #include "dump_xpc.h" |
2042 | #endif |
2043 | |
2044 | JS_PUBLIC_API(void) |
2045 | JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, |
2046 | void *thing, uint32 kind, JSBool details) |
2047 | { |
2048 | const char *name; |
2049 | size_t n; |
2050 | |
2051 | if (bufsize == 0) |
2052 | return; |
2053 | |
2054 | switch (kind) { |
2055 | case JSTRACE_OBJECT: |
2056 | { |
2057 | JSObject *obj = (JSObject *)thing; |
2058 | JSClass *clasp = STOBJ_GET_CLASS(obj); |
2059 | |
2060 | name = clasp->name; |
2061 | #ifdef HAVE_XPCONNECT |
2062 | if (clasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { |
2063 | jsval privateValue = STOBJ_GET_SLOT(obj, JSSLOT_PRIVATE); |
2064 | |
2065 | JS_ASSERT(clasp->flags & JSCLASS_HAS_PRIVATE); |
2066 | if (!JSVAL_IS_VOID(privateValue)) { |
2067 | void *privateThing = JSVAL_TO_PRIVATE(privateValue); |
2068 | const char *xpcClassName = GetXPCObjectClassName(privateThing); |
2069 | |
2070 | if (xpcClassName) |
2071 | name = xpcClassName; |
2072 | } |
2073 | } |
2074 | #endif |
2075 | break; |
2076 | } |
2077 | |
2078 | case JSTRACE_STRING: |
2079 | name = JSSTRING_IS_DEPENDENT((JSString *)thing) |
2080 | ? "substring" |
2081 | : "string"; |
2082 | break; |
2083 | |
2084 | case JSTRACE_DOUBLE: |
2085 | name = "double"; |
2086 | break; |
2087 | |
2088 | #if JS_HAS_XML_SUPPORT |
2089 | case JSTRACE_XML: |
2090 | name = "xml"; |
2091 | break; |
2092 | #endif |
2093 | default: |
2094 | JS_ASSERT(0); |
2095 | return; |
2096 | break; |
2097 | } |
2098 | |
2099 | n = strlen(name); |
2100 | if (n > bufsize - 1) |
2101 | n = bufsize - 1; |
2102 | memcpy(buf, name, n + 1); |
2103 | buf += n; |
2104 | bufsize -= n; |
2105 | |
2106 | if (details && bufsize > 2) { |
2107 | *buf++ = ' '; |
2108 | bufsize--; |
2109 | |
2110 | switch (kind) { |
2111 | case JSTRACE_OBJECT: |
2112 | { |
2113 | JSObject *obj = (JSObject *)thing; |
2114 | JSClass *clasp = STOBJ_GET_CLASS(obj); |
2115 | if (clasp == &js_FunctionClass) { |
2116 | JSFunction *fun = (JSFunction *) |
2117 | JS_GetPrivate(trc->context, obj); |
2118 | |
2119 | if (!fun) { |
2120 | JS_snprintf(buf, bufsize, "<newborn>"); |
2121 | } else if (FUN_OBJECT(fun) != obj) { |
2122 | JS_snprintf(buf, bufsize, "%p", fun); |
2123 | } else { |
2124 | if (fun->atom && ATOM_IS_STRING(fun->atom)) |
2125 | js_PutEscapedString(buf, bufsize, |
2126 | ATOM_TO_STRING(fun->atom), 0); |
2127 | } |
2128 | } else if (clasp->flags & JSCLASS_HAS_PRIVATE) { |
2129 | jsval privateValue = STOBJ_GET_SLOT(obj, JSSLOT_PRIVATE); |
2130 | void *privateThing = JSVAL_IS_VOID(privateValue) |
2131 | ? NULL |
2132 | : JSVAL_TO_PRIVATE(privateValue); |
2133 | |
2134 | JS_snprintf(buf, bufsize, "%p", privateThing); |
2135 | } else { |
2136 | JS_snprintf(buf, bufsize, "<no private>"); |
2137 | } |
2138 | break; |
2139 | } |
2140 | |
2141 | case JSTRACE_STRING: |
2142 | js_PutEscapedString(buf, bufsize, (JSString *)thing, 0); |
2143 | break; |
2144 | |
2145 | case JSTRACE_DOUBLE: |
2146 | JS_snprintf(buf, bufsize, "%g", *(jsdouble *)thing); |
2147 | break; |
2148 | |
2149 | #if JS_HAS_XML_SUPPORT |
2150 | case JSTRACE_XML: |
2151 | { |
2152 | extern const char *js_xml_class_str[]; |
2153 | JSXML *xml = (JSXML *)thing; |
2154 | |
2155 | JS_snprintf(buf, bufsize, "%s", js_xml_class_str[xml->xml_class]); |
2156 | break; |
2157 | } |
2158 | #endif |
2159 | default: |
2160 | JS_ASSERT(0); |
2161 | break; |
2162 | } |
2163 | } |
2164 | buf[bufsize - 1] = '\0'; |
2165 | } |
2166 | |
2167 | typedef struct JSHeapDumpNode JSHeapDumpNode; |
2168 | |
2169 | struct JSHeapDumpNode { |
2170 | void *thing; |
2171 | uint32 kind; |
2172 | JSHeapDumpNode *next; /* next sibling */ |
2173 | JSHeapDumpNode *parent; /* node with the thing that refer to thing |
2174 | from this node */ |
2175 | char edgeName[1]; /* name of the edge from parent->thing |
2176 | into thing */ |
2177 | }; |
2178 | |
2179 | typedef struct JSDumpingTracer { |
2180 | JSTracer base; |
2181 | JSDHashTable visited; |
2182 | JSBool ok; |
2183 | void *startThing; |
2184 | void *thingToFind; |
2185 | void *thingToIgnore; |
2186 | JSHeapDumpNode *parentNode; |
2187 | JSHeapDumpNode **lastNodep; |
2188 | char buffer[200]; |
2189 | } JSDumpingTracer; |
2190 | |
2191 | static void |
2192 | DumpNotify(JSTracer *trc, void *thing, uint32 kind) |
2193 | { |
2194 | JSDumpingTracer *dtrc; |
2195 | JSContext *cx; |
2196 | JSDHashEntryStub *entry; |
2197 | JSHeapDumpNode *node; |
2198 | const char *edgeName; |
2199 | size_t edgeNameSize; |
2200 | |
2201 | JS_ASSERT(trc->callback == DumpNotify); |
2202 | dtrc = (JSDumpingTracer *)trc; |
2203 | |
2204 | if (!dtrc->ok || thing == dtrc->thingToIgnore) |
2205 | return; |
2206 | |
2207 | cx = trc->context; |
2208 | |
2209 | /* |
2210 | * Check if we have already seen thing unless it is thingToFind to include |
2211 | * it to the graph each time we reach it and print all live things that |
2212 | * refer to thingToFind. |
2213 | * |
2214 | * This does not print all possible paths leading to thingToFind since |
2215 | * when a thing A refers directly or indirectly to thingToFind and A is |
2216 | * present several times in the graph, we will print only the first path |
2217 | * leading to A and thingToFind, other ways to reach A will be ignored. |
2218 | */ |
2219 | if (dtrc->thingToFind != thing) { |
2220 | /* |
2221 | * The startThing check allows to avoid putting startThing into the |
2222 | * hash table before tracing startThing in JS_DumpHeap. |
2223 | */ |
2224 | if (thing == dtrc->startThing) |
2225 | return; |
2226 | entry = (JSDHashEntryStub *) |
2227 | JS_DHashTableOperate(&dtrc->visited, thing, JS_DHASH_ADD); |
2228 | if (!entry) { |
2229 | JS_ReportOutOfMemory(cx); |
2230 | dtrc->ok = JS_FALSE; |
2231 | return; |
2232 | } |
2233 | if (entry->key) |
2234 | return; |
2235 | entry->key = thing; |
2236 | } |
2237 | |
2238 | if (dtrc->base.debugPrinter) { |
2239 | dtrc->base.debugPrinter(trc, dtrc->buffer, sizeof(dtrc->buffer)); |
2240 | edgeName = dtrc->buffer; |
2241 | } else if (dtrc->base.debugPrintIndex != (size_t)-1) { |
2242 | JS_snprintf(dtrc->buffer, sizeof(dtrc->buffer), "%s[%lu]", |
2243 | (const char *)dtrc->base.debugPrintArg, |
2244 | dtrc->base.debugPrintIndex); |
2245 | edgeName = dtrc->buffer; |
2246 | } else { |
2247 | edgeName = (const char*)dtrc->base.debugPrintArg; |
2248 | } |
2249 | |
2250 | edgeNameSize = strlen(edgeName) + 1; |
2251 | node = (JSHeapDumpNode *) |
2252 | JS_malloc(cx, offsetof(JSHeapDumpNode, edgeName) + edgeNameSize); |
2253 | if (!node) { |
2254 | dtrc->ok = JS_FALSE; |
2255 | return; |
2256 | } |
2257 | |
2258 | node->thing = thing; |
2259 | node->kind = kind; |
2260 | node->next = NULL; |
2261 | node->parent = dtrc->parentNode; |
2262 | memcpy(node->edgeName, edgeName, edgeNameSize); |
2263 | |
2264 | JS_ASSERT(!*dtrc->lastNodep); |
2265 | *dtrc->lastNodep = node; |
2266 | dtrc->lastNodep = &node->next; |
2267 | } |
2268 | |
2269 | /* Dump node and the chain that leads to thing it contains. */ |
2270 | static JSBool |
2271 | DumpNode(JSDumpingTracer *dtrc, FILE* fp, JSHeapDumpNode *node) |
2272 | { |
2273 | JSHeapDumpNode *prev, *following; |
2274 | size_t chainLimit; |
2275 | JSBool ok; |
2276 | enum { MAX_PARENTS_TO_PRINT = 10 }; |
2277 | |
2278 | JS_PrintTraceThingInfo(dtrc->buffer, sizeof dtrc->buffer, |
2279 | &dtrc->base, node->thing, node->kind, JS_TRUE); |
2280 | if (fprintf(fp, "%p %-22s via ", node->thing, dtrc->buffer) < 0) |
2281 | return JS_FALSE; |
2282 | |
2283 | /* |
2284 | * We need to print the parent chain in the reverse order. To do it in |
2285 | * O(N) time where N is the chain length we first reverse the chain while |
2286 | * searching for the top and then print each node while restoring the |
2287 | * chain order. |
2288 | */ |
2289 | chainLimit = MAX_PARENTS_TO_PRINT; |
2290 | prev = NULL; |
2291 | for (;;) { |
2292 | following = node->parent; |
2293 | node->parent = prev; |
2294 | prev = node; |
2295 | node = following; |
2296 | if (!node) |
2297 | break; |
2298 | if (chainLimit == 0) { |
2299 | if (fputs("...", fp) < 0) |
2300 | return JS_FALSE; |
2301 | break; |
2302 | } |
2303 | --chainLimit; |
2304 | } |
2305 | |
2306 | node = prev; |
2307 | prev = following; |
2308 | ok = JS_TRUE; |
2309 | do { |
2310 | /* Loop must continue even when !ok to restore the parent chain. */ |
2311 | if (ok) { |
2312 | if (!prev) { |
2313 | /* Print edge from some runtime root or startThing. */ |
2314 | if (fputs(node->edgeName, fp) < 0) |
2315 | ok = JS_FALSE; |
2316 | } else { |
2317 | JS_PrintTraceThingInfo(dtrc->buffer, sizeof dtrc->buffer, |
2318 | &dtrc->base, prev->thing, prev->kind, |
2319 | JS_FALSE); |
2320 | if (fprintf(fp, "(%p %s).%s", |
2321 | prev->thing, dtrc->buffer, node->edgeName) < 0) { |
2322 | ok = JS_FALSE; |
2323 | } |
2324 | } |
2325 | } |
2326 | following = node->parent; |
2327 | node->parent = prev; |
2328 | prev = node; |
2329 | node = following; |
2330 | } while (node); |
2331 | |
2332 | return ok && putc('\n', fp) >= 0; |
2333 | } |
2334 | |
2335 | JS_PUBLIC_API(JSBool) |
2336 | JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, uint32 startKind, |
2337 | void *thingToFind, size_t maxDepth, void *thingToIgnore) |
2338 | { |
2339 | JSDumpingTracer dtrc; |
2340 | JSHeapDumpNode *node, *children, *next, *parent; |
2341 | size_t depth; |
2342 | JSBool thingToFindWasTraced; |
2343 | |
2344 | if (maxDepth == 0) |
2345 | return JS_TRUE; |
2346 | |
2347 | JS_TRACER_INIT(&dtrc.base, cx, DumpNotify); |
2348 | if (!JS_DHashTableInit(&dtrc.visited, JS_DHashGetStubOps(), |
2349 | NULL, sizeof(JSDHashEntryStub), |
2350 | JS_DHASH_DEFAULT_CAPACITY(100))) { |
2351 | JS_ReportOutOfMemory(cx); |
2352 | return JS_FALSE; |
2353 | } |
2354 | dtrc.ok = JS_TRUE; |
2355 | dtrc.startThing = startThing; |
2356 | dtrc.thingToFind = thingToFind; |
2357 | dtrc.thingToIgnore = thingToIgnore; |
2358 | dtrc.parentNode = NULL; |
2359 | node = NULL; |
2360 | dtrc.lastNodep = &node; |
2361 | if (!startThing) { |
2362 | JS_ASSERT(startKind == 0); |
2363 | JS_TraceRuntime(&dtrc.base); |
2364 | } else { |
2365 | JS_TraceChildren(&dtrc.base, startThing, startKind); |
2366 | } |
2367 | |
2368 | depth = 1; |
2369 | if (!node) |
2370 | goto dump_out; |
2371 | |
2372 | thingToFindWasTraced = thingToFind && thingToFind == startThing; |
2373 | for (;;) { |
2374 | /* |
2375 | * Loop must continue even when !dtrc.ok to free all nodes allocated |
2376 | * so far. |
2377 | */ |
2378 | if (dtrc.ok) { |
2379 | if (thingToFind == NULL || thingToFind == node->thing) |
2380 | dtrc.ok = DumpNode(&dtrc, fp, node); |
2381 | |
2382 | /* Descend into children. */ |
2383 | if (dtrc.ok && |
2384 | depth < maxDepth && |
2385 | (thingToFind != node->thing || !thingToFindWasTraced)) { |
2386 | dtrc.parentNode = node; |
2387 | children = NULL; |
2388 | dtrc.lastNodep = &children; |
2389 | JS_TraceChildren(&dtrc.base, node->thing, node->kind); |
2390 | if (thingToFind == node->thing) |
2391 | thingToFindWasTraced = JS_TRUE; |
2392 | if (children != NULL) { |
2393 | ++depth; |
2394 | node = children; |
2395 | continue; |
2396 | } |
2397 | } |
2398 | } |
2399 | |
2400 | /* Move to next or parents next and free the node. */ |
2401 | for (;;) { |
2402 | next = node->next; |
2403 | parent = node->parent; |
2404 | JS_free(cx, node); |
2405 | node = next; |
2406 | if (node) |
2407 | break; |
2408 | if (!parent) |
2409 | goto dump_out; |
2410 | JS_ASSERT(depth > 1); |
2411 | --depth; |
2412 | node = parent; |
2413 | } |
2414 | } |
2415 | |
2416 | dump_out: |
2417 | JS_ASSERT(depth == 1); |
2418 | JS_DHashTableFinish(&dtrc.visited); |
2419 | return dtrc.ok; |
2420 | } |
2421 | |
2422 | #endif /* DEBUG */ |
2423 | |
2424 | JS_PUBLIC_API(void) |
2425 | JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg) |
2426 | { |
2427 | JSTracer *trc; |
2428 | |
2429 | trc = (JSTracer *)arg; |
2430 | if (!trc) |
2431 | trc = cx->runtime->gcMarkingTracer; |
2432 | else |
2433 | JS_ASSERT(trc == cx->runtime->gcMarkingTracer); |
2434 | |
2435 | #ifdef JS_THREADSAFE |
2436 | JS_ASSERT(cx->runtime->gcThread == trc->context->thread); |
2437 | #endif |
2438 | JS_SET_TRACING_NAME(trc, name ? name : "unknown"); |
2439 | js_CallValueTracerIfGCThing(trc, (jsval)thing); |
2440 | } |
2441 | |
2442 | extern JS_PUBLIC_API(JSBool) |
2443 | JS_IsGCMarkingTracer(JSTracer *trc) |
2444 | { |
2445 | return IS_GC_MARKING_TRACER(trc); |
2446 | } |
2447 | |
2448 | JS_PUBLIC_API(void) |
2449 | JS_GC(JSContext *cx) |
2450 | { |
2451 | js_LeaveTrace(cx); |
2452 | |
2453 | /* Don't nuke active arenas if executing or compiling. */ |
2454 | if (cx->stackPool.current == &cx->stackPool.first) |
2455 | JS_FinishArenaPool(&cx->stackPool); |
2456 | if (cx->tempPool.current == &cx->tempPool.first) |
2457 | JS_FinishArenaPool(&cx->tempPool); |
2458 | js_GC(cx, GC_NORMAL); |
2459 | } |
2460 | |
2461 | JS_PUBLIC_API(void) |
2462 | JS_MaybeGC(JSContext *cx) |
2463 | { |
2464 | JSRuntime *rt; |
2465 | uint32 bytes, lastBytes; |
2466 | |
2467 | rt = cx->runtime; |
2468 | |
2469 | #ifdef JS_GC_ZEAL |
2470 | if (rt->gcZeal > 0) { |
2471 | JS_GC(cx); |
2472 | return; |
2473 | } |
2474 | #endif |
2475 | |
2476 | bytes = rt->gcBytes; |
2477 | lastBytes = rt->gcLastBytes; |
2478 | |
2479 | /* |
2480 | * We run the GC if we used all available free GC cells and had to |
2481 | * allocate extra 1/3 of GC arenas since the last run of GC, or if |
2482 | * we have malloc'd more bytes through JS_malloc than we were told |
2483 | * to allocate by JS_NewRuntime. |
2484 | * |
2485 | * The reason for |
2486 | * bytes > 4/3 lastBytes |
2487 | * condition is the following. Bug 312238 changed bytes and lastBytes |
2488 | * to mean the total amount of memory that the GC uses now and right |
2489 | * after the last GC. |
2490 | * |
2491 | * Before the bug the variables meant the size of allocated GC things |
2492 | * now and right after the last GC. That size did not include the |
2493 | * memory taken by free GC cells and the condition was |
2494 | * bytes > 3/2 lastBytes. |
2495 | * That is, we run the GC if we have half again as many bytes of |
2496 | * GC-things as the last time we GC'd. To be compatible we need to |
2497 | * express that condition through the new meaning of bytes and |
2498 | * lastBytes. |
2499 | * |
2500 | * We write the original condition as |
2501 | * B*(1-F) > 3/2 Bl*(1-Fl) |
2502 | * where B is the total memory size allocated by GC and F is the free |
2503 | * cell density currently and Sl and Fl are the size and the density |
2504 | * right after GC. The density by definition is memory taken by free |
2505 | * cells divided by total amount of memory. In other words, B and Bl |
2506 | * are bytes and lastBytes with the new meaning and B*(1-F) and |
2507 | * Bl*(1-Fl) are bytes and lastBytes with the original meaning. |
2508 | * |
2509 | * Our task is to exclude F and Fl from the last statement. According |
2510 | * to the stats from bug 331966 comment 23, Fl is about 10-25% for a |
2511 | * typical run of the browser. It means that the original condition |
2512 | * implied that we did not run GC unless we exhausted the pool of |
2513 | * free cells. Indeed if we still have free cells, then B == Bl since |
2514 | * we did not yet allocated any new arenas and the condition means |
2515 | * 1 - F > 3/2 (1-Fl) or 3/2Fl > 1/2 + F |
2516 | * That implies 3/2 Fl > 1/2 or Fl > 1/3. That cannot be fulfilled |
2517 | * for the state described by the stats. So we can write the original |
2518 | * condition as: |
2519 | * F == 0 && B > 3/2 Bl(1-Fl) |
2520 | * Again using the stats we see that Fl is about 11% when the browser |
2521 | * starts up and when we are far from hitting rt->gcMaxBytes. With |
2522 | * this F we have |
2523 | * F == 0 && B > 3/2 Bl(1-0.11) |
2524 | * or approximately F == 0 && B > 4/3 Bl. |
2525 | */ |
2526 | if ((bytes > 8192 && bytes > lastBytes + lastBytes / 3) || |
2527 | rt->gcMallocBytes >= rt->gcMaxMallocBytes) { |
2528 | JS_GC(cx); |
2529 | } |
2530 | } |
2531 | |
2532 | JS_PUBLIC_API(JSGCCallback) |
2533 | JS_SetGCCallback(JSContext *cx, JSGCCallback cb) |
2534 | { |
2535 | CHECK_REQUEST(cx); |
2536 | return JS_SetGCCallbackRT(cx->runtime, cb); |
2537 | } |
2538 | |
2539 | JS_PUBLIC_API(JSGCCallback) |
2540 | JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb) |
2541 | { |
2542 | JSGCCallback oldcb; |
2543 | |
2544 | oldcb = rt->gcCallback; |
2545 | rt->gcCallback = cb; |
2546 | return oldcb; |
2547 | } |
2548 | |
2549 | JS_PUBLIC_API(JSBool) |
2550 | JS_IsAboutToBeFinalized(JSContext *cx, void *thing) |
2551 | { |
2552 | JS_ASSERT(thing); |
2553 | return js_IsAboutToBeFinalized(cx, thing); |
2554 | } |
2555 | |
2556 | JS_PUBLIC_API(void) |
2557 | JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value) |
2558 | { |
2559 | switch (key) { |
2560 | case JSGC_MAX_BYTES: |
2561 | rt->gcMaxBytes = value; |
2562 | break; |
2563 | case JSGC_MAX_MALLOC_BYTES: |
2564 | rt->gcMaxMallocBytes = value; |
2565 | break; |
2566 | case JSGC_STACKPOOL_LIFESPAN: |
2567 | rt->gcEmptyArenaPoolLifespan = value; |
2568 | break; |
2569 | default: |
2570 | JS_ASSERT(key == JSGC_TRIGGER_FACTOR); |
2571 | JS_ASSERT(value >= 100); |
2572 | rt->gcTriggerFactor = value; |
2573 | return; |
2574 | } |
2575 | } |
2576 | |
2577 | JS_PUBLIC_API(uint32) |
2578 | JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key) |
2579 | { |
2580 | switch (key) { |
2581 | case JSGC_MAX_BYTES: |
2582 | return rt->gcMaxBytes; |
2583 | case JSGC_MAX_MALLOC_BYTES: |
2584 | return rt->gcMaxMallocBytes; |
2585 | case JSGC_STACKPOOL_LIFESPAN: |
2586 | return rt->gcEmptyArenaPoolLifespan; |
2587 | case JSGC_TRIGGER_FACTOR: |
2588 | return rt->gcTriggerFactor; |
2589 | case JSGC_BYTES: |
2590 | return rt->gcBytes; |
2591 | default: |
2592 | JS_ASSERT(key == JSGC_NUMBER); |
2593 | return rt->gcNumber; |
2594 | } |
2595 | } |
2596 | |
2597 | JS_PUBLIC_API(void) |
2598 | JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32 value) |
2599 | { |
2600 | JS_ASSERT(key == JSGC_MAX_CODE_CACHE_BYTES); |
2601 | #ifdef JS_TRACER |
2602 | js_SetMaxCodeCacheBytes(cx, value); |
2603 | #endif |
2604 | } |
2605 | |
2606 | JS_PUBLIC_API(uint32) |
2607 | JS_GetGCParameterForThread(JSContext *cx, JSGCParamKey key) |
2608 | { |
2609 | JS_ASSERT(key == JSGC_MAX_CODE_CACHE_BYTES); |
2610 | #ifdef JS_TRACER |
2611 | return JS_THREAD_DATA(cx)->traceMonitor.maxCodeCacheBytes; |
2612 | #else |
2613 | return 0; |
2614 | #endif |
2615 | } |
2616 | |
2617 | JS_PUBLIC_API(intN) |
2618 | JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer) |
2619 | { |
2620 | return js_ChangeExternalStringFinalizer(NULL, finalizer); |
2621 | } |
2622 | |
2623 | JS_PUBLIC_API(intN) |
2624 | JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer) |
2625 | { |
2626 | return js_ChangeExternalStringFinalizer(finalizer, NULL); |
2627 | } |
2628 | |
2629 | JS_PUBLIC_API(JSString *) |
2630 | JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type) |
2631 | { |
2632 | JSString *str; |
2633 | |
2634 | CHECK_REQUEST(cx); |
2635 | JS_ASSERT((uintN) type < (uintN) (GCX_NTYPES - GCX_EXTERNAL_STRING)); |
2636 | |
2637 | str = (JSString *) js_NewGCThing(cx, (uintN) type + GCX_EXTERNAL_STRING, |
2638 | sizeof(JSString)); |
2639 | if (!str) |
2640 | return NULL; |
2641 | JSFLATSTR_INIT(str, chars, length); |
2642 | return str; |
2643 | } |
2644 | |
2645 | JS_PUBLIC_API(intN) |
2646 | JS_GetExternalStringGCType(JSRuntime *rt, JSString *str) |
2647 | { |
2648 | return js_GetExternalStringGCType(str); |
2649 | } |
2650 | |
2651 | JS_PUBLIC_API(void) |
2652 | JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr) |
2653 | { |
2654 | #if JS_STACK_GROWTH_DIRECTION > 0 |
2655 | if (limitAddr == 0) |
2656 | limitAddr = (jsuword)-1; |
2657 | #endif |
2658 | cx->stackLimit = limitAddr; |
2659 | } |
2660 | |
2661 | JS_PUBLIC_API(void) |
2662 | JS_SetScriptStackQuota(JSContext *cx, size_t quota) |
2663 | { |
2664 | cx->scriptStackQuota = quota; |
2665 | } |
2666 | |
2667 | /************************************************************************/ |
2668 | |
2669 | JS_PUBLIC_API(void) |
2670 | JS_DestroyIdArray(JSContext *cx, JSIdArray *ida) |
2671 | { |
2672 | JS_free(cx, ida); |
2673 | } |
2674 | |
2675 | JS_PUBLIC_API(JSBool) |
2676 | JS_ValueToId(JSContext *cx, jsval v, jsid *idp) |
2677 | { |
2678 | CHECK_REQUEST(cx); |
2679 | if (JSVAL_IS_INT(v)) |
2680 | *idp = INT_JSVAL_TO_JSID(v); |
2681 | #if JS_HAS_XML_SUPPORT |
2682 | else if (!JSVAL_IS_PRIMITIVE(v)) |
2683 | *idp = OBJECT_JSVAL_TO_JSID(v); |
2684 | #endif |
2685 | else |
2686 | return js_ValueToStringId(cx, v, idp); |
2687 | return JS_TRUE; |
2688 | } |
2689 | |
2690 | JS_PUBLIC_API(JSBool) |
2691 | JS_IdToValue(JSContext *cx, jsid id, jsval *vp) |
2692 | { |
2693 | CHECK_REQUEST(cx); |
2694 | *vp = ID_TO_VALUE(id); |
2695 | return JS_TRUE; |
2696 | } |
2697 | |
2698 | JS_PUBLIC_API(JSBool) |
2699 | JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp) |
2700 | { |
2701 | return JS_TRUE; |
2702 | } |
2703 | |
2704 | JS_PUBLIC_API(JSBool) |
2705 | JS_EnumerateStub(JSContext *cx, JSObject *obj) |
2706 | { |
2707 | return JS_TRUE; |
2708 | } |
2709 | |
2710 | JS_PUBLIC_API(JSBool) |
2711 | JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id) |
2712 | { |
2713 | return JS_TRUE; |
2714 | } |
2715 | |
2716 | JS_PUBLIC_API(JSBool) |
2717 | JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp) |
2718 | { |
2719 | return js_TryValueOf(cx, obj, type, vp); |
2720 | } |
2721 | |
2722 | JS_PUBLIC_API(void) |
2723 | JS_FinalizeStub(JSContext *cx, JSObject *obj) |
2724 | { |
2725 | } |
2726 | |
2727 | JS_PUBLIC_API(JSObject *) |
2728 | JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, |
2729 | JSClass *clasp, JSNative constructor, uintN nargs, |
2730 | JSPropertySpec *ps, JSFunctionSpec *fs, |
2731 | JSPropertySpec *static_ps, JSFunctionSpec *static_fs) |
2732 | { |
2733 | CHECK_REQUEST(cx); |
2734 | return js_InitClass(cx, obj, parent_proto, clasp, constructor, nargs, |
2735 | ps, fs, static_ps, static_fs); |
2736 | } |
2737 | |
2738 | #ifdef JS_THREADSAFE |
2739 | JS_PUBLIC_API(JSClass *) |
2740 | JS_GetClass(JSContext *cx, JSObject *obj) |
2741 | { |
2742 | return OBJ_GET_CLASS(cx, obj); |
2743 | } |
2744 | #else |
2745 | JS_PUBLIC_API(JSClass *) |
2746 | JS_GetClass(JSObject *obj) |
2747 | { |
2748 | return LOCKED_OBJ_GET_CLASS(obj); |
2749 | } |
2750 | #endif |
2751 | |
2752 | JS_PUBLIC_API(JSBool) |
2753 | JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) |
2754 | { |
2755 | JSFunction *fun; |
2756 | |
2757 | CHECK_REQUEST(cx); |
2758 | if (obj && OBJ_GET_CLASS(cx, obj) == clasp) |
2759 | return JS_TRUE; |
2760 | if (argv) { |
2761 | fun = js_ValueToFunction(cx, &argv[-2], 0); |
2762 | if (fun) { |
2763 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
2764 | JSMSG_INCOMPATIBLE_PROTO, |
2765 | clasp->name, JS_GetFunctionName(fun), |
2766 | obj |
2767 | ? OBJ_GET_CLASS(cx, obj)->name |
2768 | : js_null_str); |
2769 | } |
2770 | } |
2771 | return JS_FALSE; |
2772 | } |
2773 | |
2774 | JS_PUBLIC_API(JSBool) |
2775 | JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) |
2776 | { |
2777 | return js_HasInstance(cx, obj, v, bp); |
2778 | } |
2779 | |
2780 | JS_PUBLIC_API(void *) |
2781 | JS_GetPrivate(JSContext *cx, JSObject *obj) |
2782 | { |
2783 | jsval v; |
2784 | |
2785 | JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE); |
2786 | v = obj->fslots[JSSLOT_PRIVATE]; |
2787 | if (!JSVAL_IS_INT(v)) |
2788 | return NULL; |
2789 | return JSVAL_TO_PRIVATE(v); |
2790 | } |
2791 | |
2792 | JS_PUBLIC_API(JSBool) |
2793 | JS_SetPrivate(JSContext *cx, JSObject *obj, void *data) |
2794 | { |
2795 | JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE); |
2796 | obj->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(data); |
2797 | return JS_TRUE; |
2798 | } |
2799 | |
2800 | JS_PUBLIC_API(void *) |
2801 | JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, |
2802 | jsval *argv) |
2803 | { |
2804 | if (!JS_InstanceOf(cx, obj, clasp, argv)) |
2805 | return NULL; |
2806 | return JS_GetPrivate(cx, obj); |
2807 | } |
2808 | |
2809 | JS_PUBLIC_API(JSObject *) |
2810 | JS_GetPrototype(JSContext *cx, JSObject *obj) |
2811 | { |
2812 | JSObject *proto; |
2813 | |
2814 | CHECK_REQUEST(cx); |
2815 | proto = OBJ_GET_PROTO(cx, obj); |
2816 | |
2817 | /* Beware ref to dead object (we may be called from obj's finalizer). */ |
2818 | return proto && proto->map ? proto : NULL; |
2819 | } |
2820 | |
2821 | JS_PUBLIC_API(JSBool) |
2822 | JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto) |
2823 | { |
2824 | CHECK_REQUEST(cx); |
2825 | return js_SetProtoOrParent(cx, obj, JSSLOT_PROTO, proto, JS_FALSE); |
2826 | } |
2827 | |
2828 | JS_PUBLIC_API(JSObject *) |
2829 | JS_GetParent(JSContext *cx, JSObject *obj) |
2830 | { |
2831 | JSObject *parent; |
2832 | |
2833 | parent = OBJ_GET_PARENT(cx, obj); |
2834 | |
2835 | /* Beware ref to dead object (we may be called from obj's finalizer). */ |
2836 | return parent && parent->map ? parent : NULL; |
2837 | } |
2838 | |
2839 | JS_PUBLIC_API(JSBool) |
2840 | JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent) |
2841 | { |
2842 | CHECK_REQUEST(cx); |
2843 | return js_SetProtoOrParent(cx, obj, JSSLOT_PARENT, parent, JS_FALSE); |
2844 | } |
2845 | |
2846 | JS_PUBLIC_API(JSObject *) |
2847 | JS_GetConstructor(JSContext *cx, JSObject *proto) |
2848 | { |
2849 | jsval cval; |
2850 | |
2851 | CHECK_REQUEST(cx); |
2852 | { |
2853 | JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); |
2854 | |
2855 | if (!OBJ_GET_PROPERTY(cx, proto, |
2856 | ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), |
2857 | &cval)) { |
2858 | return NULL; |
2859 | } |
2860 | } |
2861 | if (!VALUE_IS_FUNCTION(cx, cval)) { |
2862 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, |
2863 | OBJ_GET_CLASS(cx, proto)->name); |
2864 | return NULL; |
2865 | } |
2866 | return JSVAL_TO_OBJECT(cval); |
2867 | } |
2868 | |
2869 | JS_PUBLIC_API(JSBool) |
2870 | JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp) |
2871 | { |
2872 | JS_ASSERT(JSID_IS_OBJECT(obj)); |
2873 | *idp = OBJECT_TO_JSID(obj); |
2874 | return JS_TRUE; |
2875 | } |
2876 | |
2877 | JS_PUBLIC_API(JSObject *) |
2878 | JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) |
2879 | { |
2880 | CHECK_REQUEST(cx); |
2881 | if (!clasp) |
2882 | clasp = &js_ObjectClass; /* default class is Object */ |
2883 | return js_NewObject(cx, clasp, proto, parent, 0); |
2884 | } |
2885 | |
2886 | JS_PUBLIC_API(JSObject *) |
2887 | JS_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, |
2888 | JSObject *parent) |
2889 | { |
2890 | CHECK_REQUEST(cx); |
2891 | if (!clasp) |
2892 | clasp = &js_ObjectClass; /* default class is Object */ |
2893 | return js_NewObjectWithGivenProto(cx, clasp, proto, parent, 0); |
2894 | } |
2895 | |
2896 | JS_PUBLIC_API(JSBool) |
2897 | JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep) |
2898 | { |
2899 | JSScope *scope; |
2900 | JSIdArray *ida; |
2901 | uint32 nslots, i; |
2902 | jsval v; |
2903 | |
2904 | if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_MakeArraySlow(cx, obj)) |
2905 | return JS_FALSE; |
2906 | |
2907 | if (!OBJ_IS_NATIVE(obj)) { |
2908 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
2909 | JSMSG_CANT_SEAL_OBJECT, |
2910 | OBJ_GET_CLASS(cx, obj)->name); |
2911 | return JS_FALSE; |
2912 | } |
2913 | |
2914 | scope = OBJ_SCOPE(obj); |
2915 | |
2916 | #if defined JS_THREADSAFE && defined DEBUG |
2917 | /* Insist on scope being used exclusively by cx's thread. */ |
2918 | if (scope->title.ownercx != cx) { |
2919 | JS_LOCK_OBJ(cx, obj); |
2920 | JS_ASSERT(OBJ_SCOPE(obj) == scope); |
2921 | JS_ASSERT(scope->title.ownercx == cx); |
2922 | JS_UNLOCK_SCOPE(cx, scope); |
2923 | } |
2924 | #endif |
2925 | |
2926 | /* Nothing to do if obj's scope is already sealed. */ |
2927 | if (SCOPE_IS_SEALED(scope)) |
2928 | return JS_TRUE; |
2929 | |
2930 | /* XXX Enumerate lazy properties now, as they can't be added later. */ |
2931 | ida = JS_Enumerate(cx, obj); |
2932 | if (!ida) |
2933 | return JS_FALSE; |
2934 | JS_DestroyIdArray(cx, ida); |
2935 | |
2936 | /* Ensure that obj has its own, mutable scope, and seal that scope. */ |
2937 | JS_LOCK_OBJ(cx, obj); |
2938 | scope = js_GetMutableScope(cx, obj); |
2939 | if (scope) { |
2940 | SCOPE_SET_SEALED(scope); |
2941 | js_MakeScopeShapeUnique(cx, scope); |
2942 | } |
2943 | JS_UNLOCK_OBJ(cx, obj); |
2944 | if (!scope) |
2945 | return JS_FALSE; |
2946 | |
2947 | /* If we are not sealing an entire object graph, we're done. */ |
2948 | if (!deep) |
2949 | return JS_TRUE; |
2950 | |
2951 | /* Walk slots in obj and if any value is a non-null object, seal it. */ |
2952 | nslots = scope->freeslot; |
2953 | for (i = 0; i != nslots; ++i) { |
2954 | v = STOBJ_GET_SLOT(obj, i); |
2955 | if (JSVAL_IS_PRIMITIVE(v)) |
2956 | continue; |
2957 | if (!JS_SealObject(cx, JSVAL_TO_OBJECT(v), deep)) |
2958 | return JS_FALSE; |
2959 | } |
2960 | return JS_TRUE; |
2961 | } |
2962 | |
2963 | JS_PUBLIC_API(JSObject *) |
2964 | JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, |
2965 | JSObject *parent) |
2966 | { |
2967 | CHECK_REQUEST(cx); |
2968 | if (!clasp) |
2969 | clasp = &js_ObjectClass; /* default class is Object */ |
2970 | return js_ConstructObject(cx, clasp, proto, parent, 0, NULL); |
2971 | } |
2972 | |
2973 | JS_PUBLIC_API(JSObject *) |
2974 | JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto, |
2975 | JSObject *parent, uintN argc, jsval *argv) |
2976 | { |
2977 | CHECK_REQUEST(cx); |
2978 | if (!clasp) |
2979 | clasp = &js_ObjectClass; /* default class is Object */ |
2980 | return js_ConstructObject(cx, clasp, proto, parent, argc, argv); |
2981 | } |
2982 | |
2983 | static JSBool |
2984 | DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value, |
2985 | JSPropertyOp getter, JSPropertyOp setter, uintN attrs, |
2986 | uintN flags, intN tinyid) |
2987 | { |
2988 | if (flags != 0 && OBJ_IS_NATIVE(obj)) { |
2989 | JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DECLARING); |
2990 | return !!js_DefineNativeProperty(cx, obj, id, value, getter, setter, |
2991 | attrs, flags, tinyid, NULL); |
2992 | } |
2993 | return OBJ_DEFINE_PROPERTY(cx, obj, id, value, getter, setter, attrs, |
2994 | NULL); |
2995 | } |
2996 | |
2997 | static JSBool |
2998 | DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, |
2999 | JSPropertyOp getter, JSPropertyOp setter, uintN attrs, |
3000 | uintN flags, intN tinyid) |
3001 | { |
3002 | jsid id; |
3003 | JSAtom *atom; |
3004 | |
3005 | if (attrs & JSPROP_INDEX) { |
3006 | id = INT_TO_JSID(JS_PTR_TO_INT32(name)); |
3007 | atom = NULL; |
3008 | attrs &= ~JSPROP_INDEX; |
3009 | } else { |
3010 | atom = js_Atomize(cx, name, strlen(name), 0); |
3011 | if (!atom) |
3012 | return JS_FALSE; |
3013 | id = ATOM_TO_JSID(atom); |
3014 | } |
3015 | return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, |
3016 | flags, tinyid); |
3017 | } |
3018 | |
3019 | #define AUTO_NAMELEN(s,n) (((n) == (size_t)-1) ? js_strlen(s) : (n)) |
3020 | |
3021 | static JSBool |
3022 | DefineUCProperty(JSContext *cx, JSObject *obj, |
3023 | const jschar *name, size_t namelen, jsval value, |
3024 | JSPropertyOp getter, JSPropertyOp setter, uintN attrs, |
3025 | uintN flags, intN tinyid) |
3026 | { |
3027 | JSAtom *atom; |
3028 | |
3029 | atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); |
3030 | if (!atom) |
3031 | return JS_FALSE; |
3032 | if (flags != 0 && OBJ_IS_NATIVE(obj)) { |
3033 | JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DECLARING); |
3034 | return !!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, |
3035 | getter, setter, attrs, flags, tinyid, |
3036 | NULL); |
3037 | } |
3038 | return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), value, |
3039 | getter, setter, attrs, NULL); |
3040 | } |
3041 | |
3042 | JS_PUBLIC_API(JSObject *) |
3043 | JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, |
3044 | JSObject *proto, uintN attrs) |
3045 | { |
3046 | JSObject *nobj; |
3047 | |
3048 | CHECK_REQUEST(cx); |
3049 | if (!clasp) |
3050 | clasp = &js_ObjectClass; /* default class is Object */ |
3051 | nobj = js_NewObject(cx, clasp, proto, obj, 0); |
3052 | if (!nobj) |
3053 | return NULL; |
3054 | if (!DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(nobj), NULL, NULL, attrs, |
3055 | 0, 0)) { |
3056 | cx->weakRoots.newborn[GCX_OBJECT] = NULL; |
3057 | return NULL; |
3058 | } |
3059 | return nobj; |
3060 | } |
3061 | |
3062 | JS_PUBLIC_API(JSBool) |
3063 | JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds) |
3064 | { |
3065 | JSBool ok; |
3066 | jsval value; |
3067 | uintN attrs; |
3068 | |
3069 | CHECK_REQUEST(cx); |
3070 | for (ok = JS_TRUE; cds->name; cds++) { |
3071 | ok = js_NewNumberInRootedValue(cx, cds->dval, &value); |
3072 | if (!ok) |
3073 | break; |
3074 | attrs = cds->flags; |
3075 | if (!attrs) |
3076 | attrs = JSPROP_READONLY | JSPROP_PERMANENT; |
3077 | ok = DefineProperty(cx, obj, cds->name, value, NULL, NULL, attrs, 0, 0); |
3078 | if (!ok) |
3079 | break; |
3080 | } |
3081 | return ok; |
3082 | } |
3083 | |
3084 | JS_PUBLIC_API(JSBool) |
3085 | JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps) |
3086 | { |
3087 | JSBool ok; |
3088 | |
3089 | CHECK_REQUEST(cx); |
3090 | for (ok = JS_TRUE; ps->name; ps++) { |
3091 | ok = DefineProperty(cx, obj, ps->name, JSVAL_VOID, |
3092 | ps->getter, ps->setter, ps->flags, |
3093 | SPROP_HAS_SHORTID, ps->tinyid); |
3094 | if (!ok) |
3095 | break; |
3096 | } |
3097 | return ok; |
3098 | } |
3099 | |
3100 | JS_PUBLIC_API(JSBool) |
3101 | JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, |
3102 | JSPropertyOp getter, JSPropertyOp setter, uintN attrs) |
3103 | { |
3104 | CHECK_REQUEST(cx); |
3105 | return DefineProperty(cx, obj, name, value, getter, setter, attrs, 0, 0); |
3106 | } |
3107 | |
3108 | JS_PUBLIC_API(JSBool) |
3109 | JS_DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value, |
3110 | JSPropertyOp getter, JSPropertyOp setter, uintN attrs) |
3111 | { |
3112 | CHECK_REQUEST(cx); |
3113 | return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, 0, 0); |
3114 | } |
3115 | |
3116 | JS_PUBLIC_API(JSBool) |
3117 | JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, |
3118 | int8 tinyid, jsval value, |
3119 | JSPropertyOp getter, JSPropertyOp setter, |
3120 | uintN attrs) |
3121 | { |
3122 | CHECK_REQUEST(cx); |
3123 | return DefineProperty(cx, obj, name, value, getter, setter, attrs, |
3124 | SPROP_HAS_SHORTID, tinyid); |
3125 | } |
3126 | |
3127 | static JSBool |
3128 | LookupPropertyById(JSContext *cx, JSObject *obj, jsid id, uintN flags, |
3129 | JSObject **objp, JSProperty **propp) |
3130 | { |
3131 | JSAutoResolveFlags rf(cx, flags); |
3132 | CHECK_FOR_STRING_INDEX(id); |
3133 | return OBJ_LOOKUP_PROPERTY(cx, obj, id, objp, propp); |
3134 | } |
3135 | |
3136 | static JSBool |
3137 | LookupProperty(JSContext *cx, JSObject *obj, const char *name, uintN flags, |
3138 | JSObject **objp, JSProperty **propp) |
3139 | { |
3140 | JSAtom *atom; |
3141 | |
3142 | atom = js_Atomize(cx, name, strlen(name), 0); |
3143 | if (!atom) |
3144 | return JS_FALSE; |
3145 | return LookupPropertyById(cx, obj, ATOM_TO_JSID(atom), flags, objp, propp); |
3146 | } |
3147 | |
3148 | static JSBool |
3149 | LookupUCProperty(JSContext *cx, JSObject *obj, |
3150 | const jschar *name, size_t namelen, uintN flags, |
3151 | JSObject **objp, JSProperty **propp) |
3152 | { |
3153 | JSAtom *atom; |
3154 | |
3155 | atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); |
3156 | if (!atom) |
3157 | return JS_FALSE; |
3158 | return LookupPropertyById(cx, obj, ATOM_TO_JSID(atom), flags, objp, propp); |
3159 | } |
3160 | |
3161 | JS_PUBLIC_API(JSBool) |
3162 | JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, |
3163 | const char *alias) |
3164 | { |
3165 | JSObject *obj2; |
3166 | JSProperty *prop; |
3167 | JSAtom *atom; |
3168 | JSBool ok; |
3169 | JSScopeProperty *sprop; |
3170 | |
3171 | CHECK_REQUEST(cx); |
3172 | if (!LookupProperty(cx, obj, name, JSRESOLVE_QUALIFIED, &obj2, &prop)) |
3173 | return JS_FALSE; |
3174 | if (!prop) { |
3175 | js_ReportIsNotDefined(cx, name); |
3176 | return JS_FALSE; |
3177 | } |
3178 | if (obj2 != obj || !OBJ_IS_NATIVE(obj)) { |
3179 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
3180 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS, |
3181 | alias, name, OBJ_GET_CLASS(cx, obj2)->name); |
3182 | return JS_FALSE; |
3183 | } |
3184 | atom = js_Atomize(cx, alias, strlen(alias), 0); |
3185 | if (!atom) { |
3186 | ok = JS_FALSE; |
3187 | } else { |
3188 | sprop = (JSScopeProperty *)prop; |
3189 | ok = (js_AddNativeProperty(cx, obj, ATOM_TO_JSID(atom), |
3190 | sprop->getter, sprop->setter, sprop->slot, |
3191 | sprop->attrs, sprop->flags | SPROP_IS_ALIAS, |
3192 | sprop->shortid) |
3193 | != NULL); |
3194 | } |
3195 | OBJ_DROP_PROPERTY(cx, obj, prop); |
3196 | return ok; |
3197 | } |
3198 | |
3199 | static JSBool |
3200 | LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, JSProperty *prop, |
3201 | jsval *vp) |
3202 | { |
3203 | if (!prop) { |
3204 | /* XXX bad API: no way to tell "not defined" from "void value" */ |
3205 | *vp = JSVAL_VOID; |
3206 | return JS_TRUE; |
3207 | } |
3208 | |
3209 | JSBool ok = JS_TRUE; |
3210 | if (OBJ_IS_NATIVE(obj2)) { |
3211 | JSScopeProperty *sprop = (JSScopeProperty *) prop; |
3212 | |
3213 | /* Peek at the native property's slot value, without doing a Get. */ |
3214 | *vp = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2)) |
3215 | ? LOCKED_OBJ_GET_SLOT(obj2, sprop->slot) |
3216 | : JSVAL_TRUE; |
3217 | } else if (OBJ_IS_DENSE_ARRAY(cx, obj2)) { |
3218 | ok = js_GetDenseArrayElementValue(cx, obj2, prop, vp); |
3219 | } else { |
3220 | /* XXX bad API: no way to return "defined but value unknown" */ |
3221 | *vp = JSVAL_TRUE; |
3222 | } |
3223 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
3224 | return ok; |
3225 | } |
3226 | |
3227 | static JSBool |
3228 | GetPropertyAttributesById(JSContext *cx, JSObject *obj, jsid id, uintN flags, |
3229 | JSBool own, JSPropertyDescriptor *desc) |
3230 | { |
3231 | JSObject *obj2; |
3232 | JSProperty *prop; |
3233 | JSBool ok; |
3234 | |
3235 | if (!LookupPropertyById(cx, obj, id, flags, &obj2, &prop)) |
3236 | return JS_FALSE; |
3237 | |
3238 | if (!prop || (own && obj != obj2)) { |
3239 | desc->obj = NULL; |
3240 | desc->attrs = 0; |
3241 | desc->getter = NULL; |
3242 | desc->setter = NULL; |
3243 | desc->value = JSVAL_VOID; |
3244 | if (prop) |
3245 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
3246 | return JS_TRUE; |
3247 | } |
3248 | |
3249 | desc->obj = obj2; |
3250 | |
3251 | ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &desc->attrs); |
3252 | if (ok) { |
3253 | if (OBJ_IS_NATIVE(obj2)) { |
3254 | JSScopeProperty *sprop = (JSScopeProperty *) prop; |
3255 | |
3256 | desc->getter = sprop->getter; |
3257 | desc->setter = sprop->setter; |
3258 | desc->value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2)) |
3259 | ? LOCKED_OBJ_GET_SLOT(obj2, sprop->slot) |
3260 | : JSVAL_VOID; |
3261 | } else { |
3262 | desc->getter = NULL; |
3263 | desc->setter = NULL; |
3264 | desc->value = JSVAL_VOID; |
3265 | } |
3266 | } |
3267 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
3268 | return ok; |
3269 | } |
3270 | |
3271 | static JSBool |
3272 | GetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, |
3273 | uintN *attrsp, JSBool *foundp, |
3274 | JSPropertyOp *getterp, JSPropertyOp *setterp) |
3275 | |
3276 | { |
3277 | if (!atom) |
3278 | return JS_FALSE; |
3279 | |
3280 | JSPropertyDescriptor desc; |
3281 | if (!GetPropertyAttributesById(cx, obj, ATOM_TO_JSID(atom), |
3282 | JSRESOLVE_QUALIFIED, JS_FALSE, &desc)) { |
3283 | return JS_FALSE; |
3284 | } |
3285 | |
3286 | *attrsp = desc.attrs; |
3287 | *foundp = (desc.obj != NULL); |
3288 | if (getterp) |
3289 | *getterp = desc.getter; |
3290 | if (setterp) |
3291 | *setterp = desc.setter; |
3292 | return JS_TRUE; |
3293 | } |
3294 | |
3295 | static JSBool |
3296 | SetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, |
3297 | uintN attrs, JSBool *foundp) |
3298 | { |
3299 | JSObject *obj2; |
3300 | JSProperty *prop; |
3301 | JSBool ok; |
3302 | |
3303 | if (!atom) |
3304 | return JS_FALSE; |
3305 | if (!LookupPropertyById(cx, obj, ATOM_TO_JSID(atom), JSRESOLVE_QUALIFIED, |
3306 | &obj2, &prop)) { |
3307 | return JS_FALSE; |
3308 | } |
3309 | if (!prop || obj != obj2) { |
3310 | *foundp = JS_FALSE; |
3311 | if (prop) |
3312 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
3313 | return JS_TRUE; |
3314 | } |
3315 | |
3316 | *foundp = JS_TRUE; |
3317 | ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); |
3318 | OBJ_DROP_PROPERTY(cx, obj, prop); |
3319 | return ok; |
3320 | } |
3321 | |
3322 | JS_PUBLIC_API(JSBool) |
3323 | JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, |
3324 | uintN *attrsp, JSBool *foundp) |
3325 | { |
3326 | CHECK_REQUEST(cx); |
3327 | return GetPropertyAttributes(cx, obj, |
3328 | js_Atomize(cx, name, strlen(name), 0), |
3329 | attrsp, foundp, NULL, NULL); |
3330 | } |
3331 | |
3332 | JS_PUBLIC_API(JSBool) |
3333 | JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, |
3334 | const char *name, |
3335 | uintN *attrsp, JSBool *foundp, |
3336 | JSPropertyOp *getterp, |
3337 | JSPropertyOp *setterp) |
3338 | { |
3339 | CHECK_REQUEST(cx); |
3340 | return GetPropertyAttributes(cx, obj, |
3341 | js_Atomize(cx, name, strlen(name), 0), |
3342 | attrsp, foundp, getterp, setterp); |
3343 | } |
3344 | |
3345 | JS_PUBLIC_API(JSBool) |
3346 | JS_GetPropertyAttrsGetterAndSetterById(JSContext *cx, JSObject *obj, |
3347 | jsid id, |
3348 | uintN *attrsp, JSBool *foundp, |
3349 | JSPropertyOp *getterp, |
3350 | JSPropertyOp *setterp) |
3351 | { |
3352 | CHECK_REQUEST(cx); |
3353 | |
3354 |