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

Contents of /trunk/js/jsapi.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (show annotations)
Thu Oct 23 19:03:33 2008 UTC (13 years, 8 months ago) by siliconforks
File size: 167865 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


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