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

Contents of /trunk/js/jsapi.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 585 - (show annotations)
Sun Sep 12 15:13:23 2010 UTC (8 years, 9 months ago) by siliconforks
File size: 165943 byte(s)
Update to SpiderMonkey from Firefox 3.6.9.

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