michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef vm_Xdr_h michael@0: #define vm_Xdr_h michael@0: michael@0: #include "mozilla/Endian.h" michael@0: #include "mozilla/TypeTraits.h" michael@0: michael@0: #include "jsatom.h" michael@0: michael@0: namespace js { michael@0: michael@0: /* michael@0: * Bytecode version number. Increment the subtrahend whenever JS bytecode michael@0: * changes incompatibly. michael@0: * michael@0: * This version number is XDR'd near the front of xdr bytecode and michael@0: * aborts deserialization if there is a mismatch between the current michael@0: * and saved versions. If deserialization fails, the data should be michael@0: * invalidated if possible. michael@0: */ michael@0: static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 172); michael@0: michael@0: class XDRBuffer { michael@0: public: michael@0: XDRBuffer(JSContext *cx) michael@0: : context(cx), base(nullptr), cursor(nullptr), limit(nullptr) { } michael@0: michael@0: JSContext *cx() const { michael@0: return context; michael@0: } michael@0: michael@0: void *getData(uint32_t *lengthp) const { michael@0: JS_ASSERT(size_t(cursor - base) <= size_t(UINT32_MAX)); michael@0: *lengthp = uint32_t(cursor - base); michael@0: return base; michael@0: } michael@0: michael@0: void setData(const void *data, uint32_t length) { michael@0: base = static_cast(const_cast(data)); michael@0: cursor = base; michael@0: limit = base + length; michael@0: } michael@0: michael@0: const uint8_t *read(size_t n) { michael@0: JS_ASSERT(n <= size_t(limit - cursor)); michael@0: uint8_t *ptr = cursor; michael@0: cursor += n; michael@0: return ptr; michael@0: } michael@0: michael@0: const char *readCString() { michael@0: char *ptr = reinterpret_cast(cursor); michael@0: cursor = reinterpret_cast(strchr(ptr, '\0')) + 1; michael@0: JS_ASSERT(base < cursor); michael@0: JS_ASSERT(cursor <= limit); michael@0: return ptr; michael@0: } michael@0: michael@0: uint8_t *write(size_t n) { michael@0: if (n > size_t(limit - cursor)) { michael@0: if (!grow(n)) michael@0: return nullptr; michael@0: } michael@0: uint8_t *ptr = cursor; michael@0: cursor += n; michael@0: return ptr; michael@0: } michael@0: michael@0: static bool isUint32Overflow(size_t n) { michael@0: return size_t(-1) > size_t(UINT32_MAX) && n > size_t(UINT32_MAX); michael@0: } michael@0: michael@0: void freeBuffer(); michael@0: michael@0: private: michael@0: bool grow(size_t n); michael@0: michael@0: JSContext *const context; michael@0: uint8_t *base; michael@0: uint8_t *cursor; michael@0: uint8_t *limit; michael@0: }; michael@0: michael@0: /* michael@0: * XDR serialization state. All data is encoded in little endian. michael@0: */ michael@0: template michael@0: class XDRState { michael@0: public: michael@0: XDRBuffer buf; michael@0: michael@0: protected: michael@0: JSPrincipals *originPrincipals_; michael@0: michael@0: XDRState(JSContext *cx) michael@0: : buf(cx), originPrincipals_(nullptr) { michael@0: } michael@0: michael@0: public: michael@0: JSContext *cx() const { michael@0: return buf.cx(); michael@0: } michael@0: michael@0: JSPrincipals *originPrincipals() const { michael@0: return originPrincipals_; michael@0: } michael@0: michael@0: bool codeUint8(uint8_t *n) { michael@0: if (mode == XDR_ENCODE) { michael@0: uint8_t *ptr = buf.write(sizeof *n); michael@0: if (!ptr) michael@0: return false; michael@0: *ptr = *n; michael@0: } else { michael@0: *n = *buf.read(sizeof *n); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool codeUint16(uint16_t *n) { michael@0: if (mode == XDR_ENCODE) { michael@0: uint8_t *ptr = buf.write(sizeof *n); michael@0: if (!ptr) michael@0: return false; michael@0: mozilla::LittleEndian::writeUint16(ptr, *n); michael@0: } else { michael@0: const uint8_t *ptr = buf.read(sizeof *n); michael@0: *n = mozilla::LittleEndian::readUint16(ptr); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool codeUint32(uint32_t *n) { michael@0: if (mode == XDR_ENCODE) { michael@0: uint8_t *ptr = buf.write(sizeof *n); michael@0: if (!ptr) michael@0: return false; michael@0: mozilla::LittleEndian::writeUint32(ptr, *n); michael@0: } else { michael@0: const uint8_t *ptr = buf.read(sizeof *n); michael@0: *n = mozilla::LittleEndian::readUint32(ptr); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool codeUint64(uint64_t *n) { michael@0: if (mode == XDR_ENCODE) { michael@0: uint8_t *ptr = buf.write(sizeof(*n)); michael@0: if (!ptr) michael@0: return false; michael@0: mozilla::LittleEndian::writeUint64(ptr, *n); michael@0: } else { michael@0: const uint8_t *ptr = buf.read(sizeof(*n)); michael@0: *n = mozilla::LittleEndian::readUint64(ptr); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * Use SFINAE to refuse any specialization which is not an enum. Uses of michael@0: * this function do not have to specialize the type of the enumerated field michael@0: * as C++ will extract the parameterized from the argument list. michael@0: */ michael@0: template michael@0: bool codeEnum32(T *val, typename mozilla::EnableIf::value, T>::Type * = NULL) michael@0: { michael@0: uint32_t tmp; michael@0: if (mode == XDR_ENCODE) michael@0: tmp = *val; michael@0: if (!codeUint32(&tmp)) michael@0: return false; michael@0: if (mode == XDR_DECODE) michael@0: *val = T(tmp); michael@0: return true; michael@0: } michael@0: michael@0: bool codeDouble(double *dp) { michael@0: union DoublePun { michael@0: double d; michael@0: uint64_t u; michael@0: } pun; michael@0: if (mode == XDR_ENCODE) michael@0: pun.d = *dp; michael@0: if (!codeUint64(&pun.u)) michael@0: return false; michael@0: if (mode == XDR_DECODE) michael@0: *dp = pun.d; michael@0: return true; michael@0: } michael@0: michael@0: bool codeBytes(void *bytes, size_t len) { michael@0: if (mode == XDR_ENCODE) { michael@0: uint8_t *ptr = buf.write(len); michael@0: if (!ptr) michael@0: return false; michael@0: memcpy(ptr, bytes, len); michael@0: } else { michael@0: memcpy(bytes, buf.read(len), len); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * During encoding the string is written into the buffer together with its michael@0: * terminating '\0'. During decoding the method returns a pointer into the michael@0: * decoding buffer and the caller must copy the string if it will outlive michael@0: * the decoding buffer. michael@0: */ michael@0: bool codeCString(const char **sp) { michael@0: if (mode == XDR_ENCODE) { michael@0: size_t n = strlen(*sp) + 1; michael@0: uint8_t *ptr = buf.write(n); michael@0: if (!ptr) michael@0: return false; michael@0: memcpy(ptr, *sp, n); michael@0: } else { michael@0: *sp = buf.readCString(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool codeChars(jschar *chars, size_t nchars); michael@0: michael@0: bool codeFunction(JS::MutableHandleObject objp); michael@0: bool codeScript(MutableHandleScript scriptp); michael@0: bool codeConstValue(MutableHandleValue vp); michael@0: }; michael@0: michael@0: class XDREncoder : public XDRState { michael@0: public: michael@0: XDREncoder(JSContext *cx) michael@0: : XDRState(cx) { michael@0: } michael@0: michael@0: ~XDREncoder() { michael@0: buf.freeBuffer(); michael@0: } michael@0: michael@0: const void *getData(uint32_t *lengthp) const { michael@0: return buf.getData(lengthp); michael@0: } michael@0: michael@0: void *forgetData(uint32_t *lengthp) { michael@0: void *data = buf.getData(lengthp); michael@0: buf.setData(nullptr, 0); michael@0: return data; michael@0: } michael@0: }; michael@0: michael@0: class XDRDecoder : public XDRState { michael@0: public: michael@0: XDRDecoder(JSContext *cx, const void *data, uint32_t length, michael@0: JSPrincipals *originPrincipals); michael@0: michael@0: }; michael@0: michael@0: } /* namespace js */ michael@0: michael@0: #endif /* vm_Xdr_h */