1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
16 * The Original Code is Mozilla Communicator client code, released
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.
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.
38 * ***** END LICENSE BLOCK ***** */
40 #include "jsversion.h"
53 #include "jsobj.h" /* js_XDRObject */
54 #include "jsscript.h" /* js_XDRScript */
58 #include "jsobjinlines.h"
65 #define DBG(x) ((void)0)
68 typedef struct JSXDRMemState
{
75 #define MEM_BLOCK 8192
76 #define MEM_PRIV(xdr) ((JSXDRMemState *)(xdr))
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)
82 #define MEM_LEFT(xdr, bytes) \
84 if ((xdr)->mode == JSXDR_DECODE && \
85 MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \
86 JS_ReportErrorNumber((xdr)->cx, js_GetErrorMessage, NULL, \
92 #define MEM_NEED(xdr, bytes) \
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_); \
101 MEM_BASE(xdr) = (char *) data_; \
102 MEM_LIMIT(xdr) = limit_; \
105 MEM_LEFT(xdr, bytes); \
109 #define MEM_DATA(xdr) ((void *)(MEM_BASE(xdr) + MEM_COUNT(xdr)))
110 #define MEM_INCR(xdr,bytes) (MEM_COUNT(xdr) += (bytes))
113 mem_get32(JSXDRState
*xdr
, uint32
*lp
)
116 *lp
= *(uint32
*)MEM_DATA(xdr
);
122 mem_set32(JSXDRState
*xdr
, uint32
*lp
)
125 *(uint32
*)MEM_DATA(xdr
) = *lp
;
131 mem_getbytes(JSXDRState
*xdr
, char *bytes
, uint32 len
)
134 memcpy(bytes
, MEM_DATA(xdr
), len
);
140 mem_setbytes(JSXDRState
*xdr
, char *bytes
, uint32 len
)
143 memcpy(MEM_DATA(xdr
), bytes
, len
);
149 mem_raw(JSXDRState
*xdr
, uint32 len
)
152 if (xdr
->mode
== JSXDR_ENCODE
) {
154 } else if (xdr
->mode
== JSXDR_DECODE
) {
157 data
= MEM_DATA(xdr
);
163 mem_seek(JSXDRState
*xdr
, int32 offset
, JSXDRWhence whence
)
167 if ((int32
)MEM_COUNT(xdr
) + offset
< 0) {
168 JS_ReportErrorNumber(xdr
->cx
, js_GetErrorMessage
, NULL
,
169 JSMSG_SEEK_BEYOND_START
);
173 MEM_NEED(xdr
, offset
);
174 MEM_COUNT(xdr
) += offset
;
178 JS_ReportErrorNumber(xdr
->cx
, js_GetErrorMessage
, NULL
,
179 JSMSG_SEEK_BEYOND_START
);
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
;
187 if ((uint32
)offset
> MEM_LIMIT(xdr
)) {
188 JS_ReportErrorNumber(xdr
->cx
, js_GetErrorMessage
, NULL
,
189 JSMSG_SEEK_BEYOND_END
);
192 MEM_COUNT(xdr
) = offset
;
197 xdr
->mode
== JSXDR_ENCODE
||
198 (int32
)MEM_LIMIT(xdr
) + offset
< 0) {
199 JS_ReportErrorNumber(xdr
->cx
, js_GetErrorMessage
, NULL
,
203 MEM_COUNT(xdr
) = MEM_LIMIT(xdr
) + offset
;
207 JS_snprintf(numBuf
, sizeof numBuf
, "%d", whence
);
208 JS_ReportErrorNumber(xdr
->cx
, js_GetErrorMessage
, NULL
,
209 JSMSG_WHITHER_WHENCE
, numBuf
);
216 mem_tell(JSXDRState
*xdr
)
218 return MEM_COUNT(xdr
);
222 mem_finalize(JSXDRState
*xdr
)
224 xdr
->cx
->free(MEM_BASE(xdr
));
227 static JSXDROps xdrmem_ops
= {
228 mem_get32
, mem_set32
, mem_getbytes
, mem_setbytes
,
229 mem_raw
, mem_seek
, mem_tell
, mem_finalize
233 JS_XDRInitBase(JSXDRState
*xdr
, JSXDRMode mode
, JSContext
*cx
)
237 xdr
->registry
= NULL
;
238 xdr
->numclasses
= xdr
->maxclasses
= 0;
240 xdr
->userdata
= NULL
;
244 JS_PUBLIC_API(JSXDRState
*)
245 JS_XDRNewMem(JSContext
*cx
, JSXDRMode mode
)
247 JSXDRState
*xdr
= (JSXDRState
*) cx
->malloc(sizeof(JSXDRMemState
));
250 JS_XDRInitBase(xdr
, mode
, cx
);
251 if (mode
== JSXDR_ENCODE
) {
252 if (!(MEM_BASE(xdr
) = (char *) cx
->malloc(MEM_BLOCK
))) {
257 /* XXXbe ok, so better not deref MEM_BASE(xdr) if not ENCODE */
258 MEM_BASE(xdr
) = NULL
;
260 xdr
->ops
= &xdrmem_ops
;
262 MEM_LIMIT(xdr
) = MEM_BLOCK
;
266 JS_PUBLIC_API(void *)
267 JS_XDRMemGetData(JSXDRState
*xdr
, uint32
*lp
)
269 if (xdr
->ops
!= &xdrmem_ops
)
271 *lp
= MEM_COUNT(xdr
);
272 return MEM_BASE(xdr
);
276 JS_XDRMemSetData(JSXDRState
*xdr
, void *data
, uint32 len
)
278 if (xdr
->ops
!= &xdrmem_ops
)
280 MEM_LIMIT(xdr
) = len
;
281 MEM_BASE(xdr
) = (char *) data
;
285 JS_PUBLIC_API(uint32
)
286 JS_XDRMemDataLeft(JSXDRState
*xdr
)
288 if (xdr
->ops
!= &xdrmem_ops
)
290 return MEM_LIMIT(xdr
) - MEM_COUNT(xdr
);
294 JS_XDRMemResetData(JSXDRState
*xdr
)
296 if (xdr
->ops
!= &xdrmem_ops
)
302 JS_XDRDestroy(JSXDRState
*xdr
)
304 JSContext
*cx
= xdr
->cx
;
305 xdr
->ops
->finalize(xdr
);
307 cx
->free(xdr
->registry
);
309 JS_DHashTableDestroy((JSDHashTable
*) xdr
->reghash
);
314 JS_PUBLIC_API(JSBool
)
315 JS_XDRUint8(JSXDRState
*xdr
, uint8
*b
)
318 if (!JS_XDRUint32(xdr
, &l
))
324 JS_PUBLIC_API(JSBool
)
325 JS_XDRUint16(JSXDRState
*xdr
, uint16
*s
)
328 if (!JS_XDRUint32(xdr
, &l
))
334 JS_PUBLIC_API(JSBool
)
335 JS_XDRUint32(JSXDRState
*xdr
, uint32
*lp
)
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
);
348 JS_PUBLIC_API(JSBool
)
349 JS_XDRBytes(JSXDRState
*xdr
, char *bytes
, uint32 len
)
352 static char padbuf
[JSXDR_ALIGN
-1];
354 if (xdr
->mode
== JSXDR_ENCODE
) {
355 if (!xdr
->ops
->setbytes(xdr
, bytes
, len
))
358 if (!xdr
->ops
->getbytes(xdr
, bytes
, len
))
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
))
368 if (!xdr
->ops
->seek(xdr
, padlen
, JSXDR_SEEK_CUR
))
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.
380 JS_PUBLIC_API(JSBool
)
381 JS_XDRCString(JSXDRState
*xdr
, char **sp
)
385 if (xdr
->mode
== JSXDR_ENCODE
)
387 JS_XDRUint32(xdr
, &len
);
388 if (xdr
->mode
== JSXDR_DECODE
) {
389 if (!(*sp
= (char *) xdr
->cx
->malloc(len
+ 1)))
392 if (!JS_XDRBytes(xdr
, *sp
, len
)) {
393 if (xdr
->mode
== JSXDR_DECODE
)
397 if (xdr
->mode
== JSXDR_DECODE
) {
399 } else if (xdr
->mode
== JSXDR_FREE
) {
406 JS_PUBLIC_API(JSBool
)
407 JS_XDRCStringOrNull(JSXDRState
*xdr
, char **sp
)
409 uint32 null
= (*sp
== NULL
);
410 if (!JS_XDRUint32(xdr
, &null
))
416 return JS_XDRCString(xdr
, sp
);
420 XDRChars(JSXDRState
*xdr
, jschar
*chars
, uint32 nchars
)
422 uint32 i
, padlen
, nbytes
;
425 nbytes
= nchars
* sizeof(jschar
);
426 padlen
= nbytes
% JSXDR_ALIGN
;
428 padlen
= JSXDR_ALIGN
- padlen
;
431 if (!(raw
= (jschar
*) xdr
->ops
->raw(xdr
, nbytes
)))
433 if (xdr
->mode
== JSXDR_ENCODE
) {
434 for (i
= 0; i
!= nchars
; i
++)
435 raw
[i
] = JSXDR_SWAB16(chars
[i
]);
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
]);
446 * Convert between a JS (Unicode) string and the XDR representation.
448 JS_PUBLIC_API(JSBool
)
449 JS_XDRString(JSXDRState
*xdr
, JSString
**strp
)
454 if (xdr
->mode
== JSXDR_ENCODE
)
455 nchars
= (*strp
)->length();
456 if (!JS_XDRUint32(xdr
, &nchars
))
459 if (xdr
->mode
== JSXDR_DECODE
)
460 chars
= (jschar
*) xdr
->cx
->malloc((nchars
+ 1) * sizeof(jschar
));
462 chars
= const_cast<jschar
*>((*strp
)->getChars(xdr
->cx
));
466 if (!XDRChars(xdr
, chars
, nchars
))
468 if (xdr
->mode
== JSXDR_DECODE
) {
470 *strp
= JS_NewUCString(xdr
->cx
, chars
, nchars
);
477 if (xdr
->mode
== JSXDR_DECODE
)
478 xdr
->cx
->free(chars
);
482 JS_PUBLIC_API(JSBool
)
483 JS_XDRStringOrNull(JSXDRState
*xdr
, JSString
**strp
)
485 uint32 null
= (*strp
== NULL
);
486 if (!JS_XDRUint32(xdr
, &null
))
492 return JS_XDRString(xdr
, strp
);
496 XDRDoubleValue(JSXDRState
*xdr
, jsdouble
*dp
)
500 u
.d
= (xdr
->mode
== JSXDR_ENCODE
) ? *dp
: 0.0;
501 if (!JS_XDRUint32(xdr
, &u
.s
.lo
) || !JS_XDRUint32(xdr
, &u
.s
.hi
))
503 if (xdr
->mode
== JSXDR_DECODE
)
508 JS_PUBLIC_API(JSBool
)
509 JS_XDRDouble(JSXDRState
*xdr
, jsdouble
*dp
)
511 jsdouble d
= (xdr
->mode
== JSXDR_ENCODE
) ? *dp
: 0.0;
512 if (!XDRDoubleValue(xdr
, &d
))
514 if (xdr
->mode
== JSXDR_DECODE
)
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
;
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
;
549 XDRValueBody(JSXDRState
*xdr
, uint32 type
, jsval
*vp
)
558 case XDRTAG_STRING
: {
560 if (xdr
->mode
== JSXDR_ENCODE
)
561 str
= JSVAL_TO_STRING(*vp
);
562 if (!JS_XDRString(xdr
, &str
))
564 if (xdr
->mode
== JSXDR_DECODE
)
565 *vp
= STRING_TO_JSVAL(str
);
568 case XDRTAG_DOUBLE
: {
569 double d
= xdr
->mode
== JSXDR_ENCODE
? JSVAL_TO_DOUBLE(*vp
) : 0;
570 if (!JS_XDRDouble(xdr
, &d
))
572 if (xdr
->mode
== JSXDR_DECODE
)
573 *vp
= DOUBLE_TO_JSVAL(d
);
576 case XDRTAG_OBJECT
: {
578 if (xdr
->mode
== JSXDR_ENCODE
)
579 obj
= JSVAL_TO_OBJECT(*vp
);
580 if (!js_XDRObject(xdr
, &obj
))
582 if (xdr
->mode
== JSXDR_DECODE
)
583 *vp
= OBJECT_TO_JSVAL(obj
);
586 case XDRTAG_SPECIAL
: {
588 if (xdr
->mode
== JSXDR_ENCODE
)
589 b
= (uint32
) JSVAL_TO_BOOLEAN(*vp
);
590 if (!JS_XDRUint32(xdr
, &b
))
592 if (xdr
->mode
== JSXDR_DECODE
)
593 *vp
= BOOLEAN_TO_JSVAL(!!b
);
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
))
604 if (xdr
->mode
== JSXDR_DECODE
)
605 *vp
= INT_TO_JSVAL((int32
) i
);
612 JS_PUBLIC_API(JSBool
)
613 JS_XDRValue(JSXDRState
*xdr
, jsval
*vp
)
617 if (xdr
->mode
== JSXDR_ENCODE
)
618 type
= GetXDRTag(*vp
);
619 return JS_XDRUint32(xdr
, &type
) && XDRValueBody(xdr
, type
, vp
);
623 js_XDRAtom(JSXDRState
*xdr
, JSAtom
**atomp
)
630 jschar stackChars
[256];
632 if (xdr
->mode
== JSXDR_ENCODE
) {
633 str
= ATOM_TO_STRING(*atomp
);
634 return JS_XDRString(xdr
, &str
);
638 * Inline JS_XDRString when decoding to avoid JSString allocation
639 * for already existing atoms. See bug 321985.
641 if (!JS_XDRUint32(xdr
, &nchars
))
645 if (nchars
<= JS_ARRAY_LENGTH(stackChars
)) {
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.
652 chars
= (jschar
*) cx
->malloc(nchars
* sizeof(jschar
));
657 if (XDRChars(xdr
, chars
, nchars
))
658 atom
= js_AtomizeChars(cx
, chars
, nchars
, 0);
659 if (chars
!= stackChars
)
668 JS_PUBLIC_API(JSBool
)
669 JS_XDRScript(JSXDRState
*xdr
, JSScript
**scriptp
)
671 if (!js_XDRScript(xdr
, scriptp
, NULL
))
674 if (xdr
->mode
== JSXDR_DECODE
) {
675 js_CallNewScriptHook(xdr
->cx
, *scriptp
, NULL
);
676 if (!js_NewScriptObject(xdr
->cx
, *scriptp
)) {
677 js_DestroyScript(xdr
->cx
, *scriptp
);
686 #define CLASS_REGISTRY_MIN 8
687 #define CLASS_INDEX_TO_ID(i) ((i)+1)
688 #define CLASS_ID_TO_INDEX(id) ((id)-1)
690 typedef struct JSRegHashEntry
{
696 JS_PUBLIC_API(JSBool
)
697 JS_XDRRegisterClass(JSXDRState
*xdr
, JSClass
*clasp
, uint32
*idp
)
699 uintN numclasses
, maxclasses
;
702 numclasses
= xdr
->numclasses
;
703 maxclasses
= xdr
->maxclasses
;
704 if (numclasses
== maxclasses
) {
705 maxclasses
= (maxclasses
== 0) ? CLASS_REGISTRY_MIN
: maxclasses
<< 1;
706 registry
= (JSClass
**)
707 xdr
->cx
->realloc(xdr
->registry
, maxclasses
* sizeof(JSClass
*));
710 xdr
->registry
= registry
;
711 xdr
->maxclasses
= maxclasses
;
713 JS_ASSERT(numclasses
&& numclasses
< maxclasses
);
714 registry
= xdr
->registry
;
717 registry
[numclasses
] = clasp
;
719 JSRegHashEntry
*entry
= (JSRegHashEntry
*)
720 JS_DHashTableOperate((JSDHashTable
*) xdr
->reghash
,
721 clasp
->name
, JS_DHASH_ADD
);
723 JS_ReportOutOfMemory(xdr
->cx
);
726 entry
->name
= clasp
->name
;
727 entry
->index
= numclasses
;
729 *idp
= CLASS_INDEX_TO_ID(numclasses
);
730 xdr
->numclasses
= ++numclasses
;
734 JS_PUBLIC_API(uint32
)
735 JS_XDRFindClassIdByName(JSXDRState
*xdr
, const char *name
)
739 numclasses
= xdr
->numclasses
;
740 if (numclasses
>= 10) {
741 JSRegHashEntry
*entry
;
743 /* Bootstrap reghash from registry on first overpopulated Find. */
746 JS_NewDHashTable(JS_DHashGetStubOps(), NULL
,
747 sizeof(JSRegHashEntry
),
748 JS_DHASH_DEFAULT_CAPACITY(numclasses
));
750 for (i
= 0; i
< numclasses
; i
++) {
751 JSClass
*clasp
= xdr
->registry
[i
];
752 entry
= (JSRegHashEntry
*)
753 JS_DHashTableOperate((JSDHashTable
*) xdr
->reghash
,
754 clasp
->name
, JS_DHASH_ADD
);
755 entry
->name
= clasp
->name
;
761 /* If we managed to create reghash, use it for O(1) Find. */
763 entry
= (JSRegHashEntry
*)
764 JS_DHashTableOperate((JSDHashTable
*) xdr
->reghash
,
765 name
, JS_DHASH_LOOKUP
);
766 if (JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
))
767 return CLASS_INDEX_TO_ID(entry
->index
);
771 /* Only a few classes, or we couldn't malloc reghash: use linear search. */
772 for (i
= 0; i
< numclasses
; i
++) {
773 if (!strcmp(name
, xdr
->registry
[i
]->name
))
774 return CLASS_INDEX_TO_ID(i
);
779 JS_PUBLIC_API(JSClass
*)
780 JS_XDRFindClassById(JSXDRState
*xdr
, uint32 id
)
782 uintN i
= CLASS_ID_TO_INDEX(id
);
784 if (i
>= xdr
->numclasses
)
786 return xdr
->registry
[i
];
789 #endif /* JS_HAS_XDR */