/[jscoverage]/trunk/js/jsapi.cpp
ViewVC logotype

Contents of /trunk/js/jsapi.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 460 - (show annotations)
Sat Sep 26 23:15:22 2009 UTC (12 years, 9 months ago) by siliconforks
File size: 168805 byte(s)
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