do-not-enforce-wrong-arm-flags
[platform/upstream/js.git] / js / src / jsxdrapi.cpp
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
39
40 #include "jsversion.h"
41
42 #if JS_HAS_XDR
43
44 #include <string.h>
45 #include "jstypes.h"
46 #include "jsstdint.h"
47 #include "jsutil.h"
48 #include "jsdhash.h"
49 #include "jsprf.h"
50 #include "jsapi.h"
51 #include "jscntxt.h"
52 #include "jsnum.h"
53 #include "jsobj.h"              /* js_XDRObject */
54 #include "jsscript.h"           /* js_XDRScript */
55 #include "jsstr.h"
56 #include "jsxdrapi.h"
57
58 #include "jsobjinlines.h"
59
60 using namespace js;
61
62 #ifdef DEBUG
63 #define DBG(x) x
64 #else
65 #define DBG(x) ((void)0)
66 #endif
67
68 typedef struct JSXDRMemState {
69     JSXDRState  state;
70     char        *base;
71     uint32      count;
72     uint32      limit;
73 } JSXDRMemState;
74
75 #define MEM_BLOCK       8192
76 #define MEM_PRIV(xdr)   ((JSXDRMemState *)(xdr))
77
78 #define MEM_BASE(xdr)   (MEM_PRIV(xdr)->base)
79 #define MEM_COUNT(xdr)  (MEM_PRIV(xdr)->count)
80 #define MEM_LIMIT(xdr)  (MEM_PRIV(xdr)->limit)
81
82 #define MEM_LEFT(xdr, bytes)                                                  \
83     JS_BEGIN_MACRO                                                            \
84         if ((xdr)->mode == JSXDR_DECODE &&                                    \
85             MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) {                        \
86             JS_ReportErrorNumber((xdr)->cx, js_GetErrorMessage, NULL,         \
87                                  JSMSG_END_OF_DATA);                          \
88             return 0;                                                         \
89         }                                                                     \
90     JS_END_MACRO
91
92 #define MEM_NEED(xdr, bytes)                                                  \
93     JS_BEGIN_MACRO                                                            \
94         if ((xdr)->mode == JSXDR_ENCODE) {                                    \
95             if (MEM_LIMIT(xdr) &&                                             \
96                 MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) {                    \
97                 uint32 limit_ = JS_ROUNDUP(MEM_COUNT(xdr) + bytes, MEM_BLOCK);\
98                 void *data_ = (xdr)->cx->realloc(MEM_BASE(xdr), limit_);      \
99                 if (!data_)                                                   \
100                     return 0;                                                 \
101                 MEM_BASE(xdr) = (char *) data_;                               \
102                 MEM_LIMIT(xdr) = limit_;                                      \
103             }                                                                 \
104         } else {                                                              \
105             MEM_LEFT(xdr, bytes);                                             \
106         }                                                                     \
107     JS_END_MACRO
108
109 #define MEM_DATA(xdr)        ((void *)(MEM_BASE(xdr) + MEM_COUNT(xdr)))
110 #define MEM_INCR(xdr,bytes)  (MEM_COUNT(xdr) += (bytes))
111
112 static JSBool
113 mem_get32(JSXDRState *xdr, uint32 *lp)
114 {
115     MEM_LEFT(xdr, 4);
116     *lp = *(uint32 *)MEM_DATA(xdr);
117     MEM_INCR(xdr, 4);
118     return JS_TRUE;
119 }
120
121 static JSBool
122 mem_set32(JSXDRState *xdr, uint32 *lp)
123 {
124     MEM_NEED(xdr, 4);
125     *(uint32 *)MEM_DATA(xdr) = *lp;
126     MEM_INCR(xdr, 4);
127     return JS_TRUE;
128 }
129
130 static JSBool
131 mem_getbytes(JSXDRState *xdr, char *bytes, uint32 len)
132 {
133     MEM_LEFT(xdr, len);
134     memcpy(bytes, MEM_DATA(xdr), len);
135     MEM_INCR(xdr, len);
136     return JS_TRUE;
137 }
138
139 static JSBool
140 mem_setbytes(JSXDRState *xdr, char *bytes, uint32 len)
141 {
142     MEM_NEED(xdr, len);
143     memcpy(MEM_DATA(xdr), bytes, len);
144     MEM_INCR(xdr, len);
145     return JS_TRUE;
146 }
147
148 static void *
149 mem_raw(JSXDRState *xdr, uint32 len)
150 {
151     void *data;
152     if (xdr->mode == JSXDR_ENCODE) {
153         MEM_NEED(xdr, len);
154     } else if (xdr->mode == JSXDR_DECODE) {
155         MEM_LEFT(xdr, len);
156     }
157     data = MEM_DATA(xdr);
158     MEM_INCR(xdr, len);
159     return data;
160 }
161
162 static JSBool
163 mem_seek(JSXDRState *xdr, int32 offset, JSXDRWhence whence)
164 {
165     switch (whence) {
166       case JSXDR_SEEK_CUR:
167         if ((int32)MEM_COUNT(xdr) + offset < 0) {
168             JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,
169                                  JSMSG_SEEK_BEYOND_START);
170             return JS_FALSE;
171         }
172         if (offset > 0)
173             MEM_NEED(xdr, offset);
174         MEM_COUNT(xdr) += offset;
175         return JS_TRUE;
176       case JSXDR_SEEK_SET:
177         if (offset < 0) {
178             JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,
179                                  JSMSG_SEEK_BEYOND_START);
180             return JS_FALSE;
181         }
182         if (xdr->mode == JSXDR_ENCODE) {
183             if ((uint32)offset > MEM_COUNT(xdr))
184                 MEM_NEED(xdr, offset - MEM_COUNT(xdr));
185             MEM_COUNT(xdr) = offset;
186         } else {
187             if ((uint32)offset > MEM_LIMIT(xdr)) {
188                 JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,
189                                      JSMSG_SEEK_BEYOND_END);
190                 return JS_FALSE;
191             }
192             MEM_COUNT(xdr) = offset;
193         }
194         return JS_TRUE;
195       case JSXDR_SEEK_END:
196         if (offset >= 0 ||
197             xdr->mode == JSXDR_ENCODE ||
198             (int32)MEM_LIMIT(xdr) + offset < 0) {
199             JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,
200                                  JSMSG_END_SEEK);
201             return JS_FALSE;
202         }
203         MEM_COUNT(xdr) = MEM_LIMIT(xdr) + offset;
204         return JS_TRUE;
205       default: {
206         char numBuf[12];
207         JS_snprintf(numBuf, sizeof numBuf, "%d", whence);
208         JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,
209                              JSMSG_WHITHER_WHENCE, numBuf);
210         return JS_FALSE;
211       }
212     }
213 }
214
215 static uint32
216 mem_tell(JSXDRState *xdr)
217 {
218     return MEM_COUNT(xdr);
219 }
220
221 static void
222 mem_finalize(JSXDRState *xdr)
223 {
224     xdr->cx->free(MEM_BASE(xdr));
225 }
226
227 static JSXDROps xdrmem_ops = {
228     mem_get32,      mem_set32,      mem_getbytes,   mem_setbytes,
229     mem_raw,        mem_seek,       mem_tell,       mem_finalize
230 };
231
232 JS_PUBLIC_API(void)
233 JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx)
234 {
235     xdr->mode = mode;
236     xdr->cx = cx;
237     xdr->registry = NULL;
238     xdr->numclasses = xdr->maxclasses = 0;
239     xdr->reghash = NULL;
240     xdr->userdata = NULL;
241     xdr->script = NULL;
242 }
243
244 JS_PUBLIC_API(JSXDRState *)
245 JS_XDRNewMem(JSContext *cx, JSXDRMode mode)
246 {
247     JSXDRState *xdr = (JSXDRState *) cx->malloc(sizeof(JSXDRMemState));
248     if (!xdr)
249         return NULL;
250     JS_XDRInitBase(xdr, mode, cx);
251     if (mode == JSXDR_ENCODE) {
252         if (!(MEM_BASE(xdr) = (char *) cx->malloc(MEM_BLOCK))) {
253             cx->free(xdr);
254             return NULL;
255         }
256     } else {
257         /* XXXbe ok, so better not deref MEM_BASE(xdr) if not ENCODE */
258         MEM_BASE(xdr) = NULL;
259     }
260     xdr->ops = &xdrmem_ops;
261     MEM_COUNT(xdr) = 0;
262     MEM_LIMIT(xdr) = MEM_BLOCK;
263     return xdr;
264 }
265
266 JS_PUBLIC_API(void *)
267 JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp)
268 {
269     if (xdr->ops != &xdrmem_ops)
270         return NULL;
271     *lp = MEM_COUNT(xdr);
272     return MEM_BASE(xdr);
273 }
274
275 JS_PUBLIC_API(void)
276 JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len)
277 {
278     if (xdr->ops != &xdrmem_ops)
279         return;
280     MEM_LIMIT(xdr) = len;
281     MEM_BASE(xdr) = (char *) data;
282     MEM_COUNT(xdr) = 0;
283 }
284
285 JS_PUBLIC_API(uint32)
286 JS_XDRMemDataLeft(JSXDRState *xdr)
287 {
288     if (xdr->ops != &xdrmem_ops)
289         return 0;
290     return MEM_LIMIT(xdr) - MEM_COUNT(xdr);
291 }
292
293 JS_PUBLIC_API(void)
294 JS_XDRMemResetData(JSXDRState *xdr)
295 {
296     if (xdr->ops != &xdrmem_ops)
297         return;
298     MEM_COUNT(xdr) = 0;
299 }
300
301 JS_PUBLIC_API(void)
302 JS_XDRDestroy(JSXDRState *xdr)
303 {
304     JSContext *cx = xdr->cx;
305     xdr->ops->finalize(xdr);
306     if (xdr->registry) {
307         cx->free(xdr->registry);
308         if (xdr->reghash)
309             JS_DHashTableDestroy((JSDHashTable *) xdr->reghash);
310     }
311     cx->free(xdr);
312 }
313
314 JS_PUBLIC_API(JSBool)
315 JS_XDRUint8(JSXDRState *xdr, uint8 *b)
316 {
317     uint32 l = *b;
318     if (!JS_XDRUint32(xdr, &l))
319         return JS_FALSE;
320     *b = (uint8) l;
321     return JS_TRUE;
322 }
323
324 JS_PUBLIC_API(JSBool)
325 JS_XDRUint16(JSXDRState *xdr, uint16 *s)
326 {
327     uint32 l = *s;
328     if (!JS_XDRUint32(xdr, &l))
329         return JS_FALSE;
330     *s = (uint16) l;
331     return JS_TRUE;
332 }
333
334 JS_PUBLIC_API(JSBool)
335 JS_XDRUint32(JSXDRState *xdr, uint32 *lp)
336 {
337     JSBool ok = JS_TRUE;
338     if (xdr->mode == JSXDR_ENCODE) {
339         uint32 xl = JSXDR_SWAB32(*lp);
340         ok = xdr->ops->set32(xdr, &xl);
341     } else if (xdr->mode == JSXDR_DECODE) {
342         ok = xdr->ops->get32(xdr, lp);
343         *lp = JSXDR_SWAB32(*lp);
344     }
345     return ok;
346 }
347
348 JS_PUBLIC_API(JSBool)
349 JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len)
350 {
351     uint32 padlen;
352     static char padbuf[JSXDR_ALIGN-1];
353
354     if (xdr->mode == JSXDR_ENCODE) {
355         if (!xdr->ops->setbytes(xdr, bytes, len))
356             return JS_FALSE;
357     } else {
358         if (!xdr->ops->getbytes(xdr, bytes, len))
359             return JS_FALSE;
360     }
361     len = xdr->ops->tell(xdr);
362     if (len % JSXDR_ALIGN) {
363         padlen = JSXDR_ALIGN - (len % JSXDR_ALIGN);
364         if (xdr->mode == JSXDR_ENCODE) {
365             if (!xdr->ops->setbytes(xdr, padbuf, padlen))
366                 return JS_FALSE;
367         } else {
368             if (!xdr->ops->seek(xdr, padlen, JSXDR_SEEK_CUR))
369                 return JS_FALSE;
370         }
371     }
372     return JS_TRUE;
373 }
374
375 /**
376  * Convert between a C string and the XDR representation:
377  * leading 32-bit count, then counted vector of chars,
378  * then possibly \0 padding to multiple of 4.
379  */
380 JS_PUBLIC_API(JSBool)
381 JS_XDRCString(JSXDRState *xdr, char **sp)
382 {
383     uint32 len;
384
385     if (xdr->mode == JSXDR_ENCODE)
386         len = strlen(*sp);
387     JS_XDRUint32(xdr, &len);
388     if (xdr->mode == JSXDR_DECODE) {
389         if (!(*sp = (char *) xdr->cx->malloc(len + 1)))
390             return JS_FALSE;
391     }
392     if (!JS_XDRBytes(xdr, *sp, len)) {
393         if (xdr->mode == JSXDR_DECODE)
394             xdr->cx->free(*sp);
395         return JS_FALSE;
396     }
397     if (xdr->mode == JSXDR_DECODE) {
398         (*sp)[len] = '\0';
399     } else if (xdr->mode == JSXDR_FREE) {
400         xdr->cx->free(*sp);
401         *sp = NULL;
402     }
403     return JS_TRUE;
404 }
405
406 JS_PUBLIC_API(JSBool)
407 JS_XDRCStringOrNull(JSXDRState *xdr, char **sp)
408 {
409     uint32 null = (*sp == NULL);
410     if (!JS_XDRUint32(xdr, &null))
411         return JS_FALSE;
412     if (null) {
413         *sp = NULL;
414         return JS_TRUE;
415     }
416     return JS_XDRCString(xdr, sp);
417 }
418
419 static JSBool
420 XDRChars(JSXDRState *xdr, jschar *chars, uint32 nchars)
421 {
422     uint32 i, padlen, nbytes;
423     jschar *raw;
424
425     nbytes = nchars * sizeof(jschar);
426     padlen = nbytes % JSXDR_ALIGN;
427     if (padlen) {
428         padlen = JSXDR_ALIGN - padlen;
429         nbytes += padlen;
430     }
431     if (!(raw = (jschar *) xdr->ops->raw(xdr, nbytes)))
432         return JS_FALSE;
433     if (xdr->mode == JSXDR_ENCODE) {
434         for (i = 0; i != nchars; i++)
435             raw[i] = JSXDR_SWAB16(chars[i]);
436         if (padlen)
437             memset((char *)raw + nbytes - padlen, 0, padlen);
438     } else if (xdr->mode == JSXDR_DECODE) {
439         for (i = 0; i != nchars; i++)
440             chars[i] = JSXDR_SWAB16(raw[i]);
441     }
442     return JS_TRUE;
443 }
444
445 /*
446  * Convert between a JS (Unicode) string and the XDR representation.
447  */
448 JS_PUBLIC_API(JSBool)
449 JS_XDRString(JSXDRState *xdr, JSString **strp)
450 {
451     uint32 nchars;
452     jschar *chars;
453
454     if (xdr->mode == JSXDR_ENCODE)
455         nchars = (*strp)->length();
456     if (!JS_XDRUint32(xdr, &nchars))
457         return JS_FALSE;
458
459     if (xdr->mode == JSXDR_DECODE)
460         chars = (jschar *) xdr->cx->malloc((nchars + 1) * sizeof(jschar));
461     else
462         chars = const_cast<jschar *>((*strp)->getChars(xdr->cx));
463     if (!chars)
464         return JS_FALSE;
465
466     if (!XDRChars(xdr, chars, nchars))
467         goto bad;
468     if (xdr->mode == JSXDR_DECODE) {
469         chars[nchars] = 0;
470         *strp = JS_NewUCString(xdr->cx, chars, nchars);
471         if (!*strp)
472             goto bad;
473     }
474     return JS_TRUE;
475
476 bad:
477     if (xdr->mode == JSXDR_DECODE)
478         xdr->cx->free(chars);
479     return JS_FALSE;
480 }
481
482 JS_PUBLIC_API(JSBool)
483 JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp)
484 {
485     uint32 null = (*strp == NULL);
486     if (!JS_XDRUint32(xdr, &null))
487         return JS_FALSE;
488     if (null) {
489         *strp = NULL;
490         return JS_TRUE;
491     }
492     return JS_XDRString(xdr, strp);
493 }
494
495 static JSBool
496 XDRDoubleValue(JSXDRState *xdr, jsdouble *dp)
497 {
498     jsdpun u;
499
500     u.d = (xdr->mode == JSXDR_ENCODE) ? *dp : 0.0;
501     if (!JS_XDRUint32(xdr, &u.s.lo) || !JS_XDRUint32(xdr, &u.s.hi))
502         return JS_FALSE;
503     if (xdr->mode == JSXDR_DECODE)
504         *dp = u.d;
505     return JS_TRUE;
506 }
507
508 JS_PUBLIC_API(JSBool)
509 JS_XDRDouble(JSXDRState *xdr, jsdouble *dp)
510 {
511     jsdouble d = (xdr->mode == JSXDR_ENCODE) ? *dp : 0.0;
512     if (!XDRDoubleValue(xdr, &d))
513         return JS_FALSE;
514     if (xdr->mode == JSXDR_DECODE)
515         *dp = d;
516     return JS_TRUE;
517 }
518
519 enum XDRValueTag {
520     XDRTAG_OBJECT  = 0,
521     XDRTAG_INT     = 1,
522     XDRTAG_DOUBLE  = 2,
523     XDRTAG_STRING  = 3,
524     XDRTAG_SPECIAL = 4,
525     XDRTAG_XDRNULL = 5,
526     XDRTAG_XDRVOID = 6
527 };
528
529 static XDRValueTag
530 GetXDRTag(jsval v)
531 {
532     if (JSVAL_IS_NULL(v))
533         return XDRTAG_XDRNULL;
534     if (JSVAL_IS_VOID(v))
535         return XDRTAG_XDRVOID;
536     if (JSVAL_IS_OBJECT(v))
537         return XDRTAG_OBJECT;
538     if (JSVAL_IS_INT(v))
539         return XDRTAG_INT;
540     if (JSVAL_IS_DOUBLE(v))
541         return XDRTAG_DOUBLE;
542     if (JSVAL_IS_STRING(v))
543         return XDRTAG_STRING;
544     JS_ASSERT(JSVAL_IS_BOOLEAN(v));
545     return XDRTAG_SPECIAL;
546 }
547
548 static JSBool
549 XDRValueBody(JSXDRState *xdr, uint32 type, jsval *vp)
550 {
551     switch (type) {
552       case XDRTAG_XDRNULL:
553         *vp = JSVAL_NULL;
554         break;
555       case XDRTAG_XDRVOID:
556         *vp = JSVAL_VOID;
557         break;
558       case XDRTAG_STRING: {
559         JSString *str;
560         if (xdr->mode == JSXDR_ENCODE)
561             str = JSVAL_TO_STRING(*vp);
562         if (!JS_XDRString(xdr, &str))
563             return JS_FALSE;
564         if (xdr->mode == JSXDR_DECODE)
565             *vp = STRING_TO_JSVAL(str);
566         break;
567       }
568       case XDRTAG_DOUBLE: {
569         double d = xdr->mode == JSXDR_ENCODE ? JSVAL_TO_DOUBLE(*vp) : 0;
570         if (!JS_XDRDouble(xdr, &d))
571             return JS_FALSE;
572         if (xdr->mode == JSXDR_DECODE)
573             *vp = DOUBLE_TO_JSVAL(d);
574         break;
575       }
576       case XDRTAG_OBJECT: {
577         JSObject *obj;
578         if (xdr->mode == JSXDR_ENCODE)
579             obj = JSVAL_TO_OBJECT(*vp);
580         if (!js_XDRObject(xdr, &obj))
581             return JS_FALSE;
582         if (xdr->mode == JSXDR_DECODE)
583             *vp = OBJECT_TO_JSVAL(obj);
584         break;
585       }
586       case XDRTAG_SPECIAL: {
587         uint32 b;
588         if (xdr->mode == JSXDR_ENCODE)
589             b = (uint32) JSVAL_TO_BOOLEAN(*vp);
590         if (!JS_XDRUint32(xdr, &b))
591             return JS_FALSE;
592         if (xdr->mode == JSXDR_DECODE)
593             *vp = BOOLEAN_TO_JSVAL(!!b);
594         break;
595       }
596       default: {
597         uint32 i;
598
599         JS_ASSERT(type == XDRTAG_INT);
600         if (xdr->mode == JSXDR_ENCODE)
601             i = (uint32) JSVAL_TO_INT(*vp);
602         if (!JS_XDRUint32(xdr, &i))
603             return JS_FALSE;
604         if (xdr->mode == JSXDR_DECODE)
605             *vp = INT_TO_JSVAL((int32) i);
606         break;
607       }
608     }
609     return JS_TRUE;
610 }
611
612 JS_PUBLIC_API(JSBool)
613 JS_XDRValue(JSXDRState *xdr, jsval *vp)
614 {
615     uint32 type;
616
617     if (xdr->mode == JSXDR_ENCODE)
618         type = GetXDRTag(*vp);
619     return JS_XDRUint32(xdr, &type) && XDRValueBody(xdr, type, vp);
620 }
621
622 extern JSBool
623 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp)
624 {
625     JSString *str;
626     uint32 nchars;
627     JSAtom *atom;
628     JSContext *cx;
629     jschar *chars;
630     jschar stackChars[256];
631
632     if (xdr->mode == JSXDR_ENCODE) {
633         str = ATOM_TO_STRING(*atomp);
634         return JS_XDRString(xdr, &str);
635     }
636
637     /*
638      * Inline JS_XDRString when decoding to avoid JSString allocation
639      * for already existing atoms. See bug 321985.
640      */
641     if (!JS_XDRUint32(xdr, &nchars))
642         return JS_FALSE;
643     atom = NULL;
644     cx = xdr->cx;
645     if (nchars <= JS_ARRAY_LENGTH(stackChars)) {
646         chars = stackChars;
647     } else {
648         /*
649          * This is very uncommon. Don't use the tempPool arena for this as
650          * most allocations here will be bigger than tempPool's arenasize.
651          */
652         chars = (jschar *) cx->malloc(nchars * sizeof(jschar));
653         if (!chars)
654             return JS_FALSE;
655     }
656
657     if (XDRChars(xdr, chars, nchars))
658         atom = js_AtomizeChars(cx, chars, nchars, 0);
659     if (chars != stackChars)
660         cx->free(chars);
661
662     if (!atom)
663         return JS_FALSE;
664     *atomp = atom;
665     return JS_TRUE;
666 }
667
668 JS_PUBLIC_API(JSBool)
669 JS_XDRScriptObject(JSXDRState *xdr, JSObject **scriptObjp)
670 {
671     JSScript *script;
672     if (xdr->mode == JSXDR_DECODE) {
673         script = NULL;
674         *scriptObjp = NULL;
675     } else {
676         script = (*scriptObjp)->getScript();
677     }
678     
679     if (!js_XDRScript(xdr, &script, NULL))
680         return false;
681
682     if (xdr->mode == JSXDR_DECODE) {
683         js_CallNewScriptHook(xdr->cx, script, NULL);
684         *scriptObjp = js_NewScriptObject(xdr->cx, script);
685         if (!*scriptObjp) {
686             js_DestroyScript(xdr->cx, script);
687             return false;
688         }
689     }
690
691     return true;
692 }
693
694 #define CLASS_REGISTRY_MIN      8
695 #define CLASS_INDEX_TO_ID(i)    ((i)+1)
696 #define CLASS_ID_TO_INDEX(id)   ((id)-1)
697
698 typedef struct JSRegHashEntry {
699     JSDHashEntryHdr hdr;
700     const char      *name;
701     uint32          index;
702 } JSRegHashEntry;
703
704 JS_PUBLIC_API(JSBool)
705 JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *idp)
706 {
707     uintN numclasses, maxclasses;
708     JSClass **registry;
709
710     numclasses = xdr->numclasses;
711     maxclasses = xdr->maxclasses;
712     if (numclasses == maxclasses) {
713         maxclasses = (maxclasses == 0) ? CLASS_REGISTRY_MIN : maxclasses << 1;
714         registry = (JSClass **)
715             xdr->cx->realloc(xdr->registry, maxclasses * sizeof(JSClass *));
716         if (!registry)
717             return JS_FALSE;
718         xdr->registry = registry;
719         xdr->maxclasses = maxclasses;
720     } else {
721         JS_ASSERT(numclasses && numclasses < maxclasses);
722         registry = xdr->registry;
723     }
724
725     registry[numclasses] = clasp;
726     if (xdr->reghash) {
727         JSRegHashEntry *entry = (JSRegHashEntry *)
728             JS_DHashTableOperate((JSDHashTable *) xdr->reghash,
729                                  clasp->name, JS_DHASH_ADD);
730         if (!entry) {
731             JS_ReportOutOfMemory(xdr->cx);
732             return JS_FALSE;
733         }
734         entry->name = clasp->name;
735         entry->index = numclasses;
736     }
737     *idp = CLASS_INDEX_TO_ID(numclasses);
738     xdr->numclasses = ++numclasses;
739     return JS_TRUE;
740 }
741
742 JS_PUBLIC_API(uint32)
743 JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name)
744 {
745     uintN i, numclasses;
746
747     numclasses = xdr->numclasses;
748     if (numclasses >= 10) {
749         JSRegHashEntry *entry;
750
751         /* Bootstrap reghash from registry on first overpopulated Find. */
752         if (!xdr->reghash) {
753             xdr->reghash =
754                 JS_NewDHashTable(JS_DHashGetStubOps(), NULL,
755                                  sizeof(JSRegHashEntry),
756                                  JS_DHASH_DEFAULT_CAPACITY(numclasses));
757             if (xdr->reghash) {
758                 for (i = 0; i < numclasses; i++) {
759                     JSClass *clasp = xdr->registry[i];
760                     entry = (JSRegHashEntry *)
761                         JS_DHashTableOperate((JSDHashTable *) xdr->reghash,
762                                              clasp->name, JS_DHASH_ADD);
763                     entry->name = clasp->name;
764                     entry->index = i;
765                 }
766             }
767         }
768
769         /* If we managed to create reghash, use it for O(1) Find. */
770         if (xdr->reghash) {
771             entry = (JSRegHashEntry *)
772                 JS_DHashTableOperate((JSDHashTable *) xdr->reghash,
773                                      name, JS_DHASH_LOOKUP);
774             if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr))
775                 return CLASS_INDEX_TO_ID(entry->index);
776         }
777     }
778
779     /* Only a few classes, or we couldn't malloc reghash: use linear search. */
780     for (i = 0; i < numclasses; i++) {
781         if (!strcmp(name, xdr->registry[i]->name))
782             return CLASS_INDEX_TO_ID(i);
783     }
784     return 0;
785 }
786
787 JS_PUBLIC_API(JSClass *)
788 JS_XDRFindClassById(JSXDRState *xdr, uint32 id)
789 {
790     uintN i = CLASS_ID_TO_INDEX(id);
791
792     if (i >= xdr->numclasses)
793         return NULL;
794     return xdr->registry[i];
795 }
796
797 #endif /* JS_HAS_XDR */