diff options
Diffstat (limited to 'js/src/vm/Xdr.cpp')
-rw-r--r-- | js/src/vm/Xdr.cpp | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/js/src/vm/Xdr.cpp b/js/src/vm/Xdr.cpp new file mode 100644 index 0000000000..0bf2adcd33 --- /dev/null +++ b/js/src/vm/Xdr.cpp @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "vm/Xdr.h" + +#include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF +#include "mozilla/EndianUtils.h" // mozilla::NativeEndian, MOZ_LITTLE_ENDIAN +#include "mozilla/Result.h" // mozilla::{Result, Ok, Err}, MOZ_TRY +#include "mozilla/Utf8.h" // mozilla::Utf8Unit + +#include <algorithm> // std::transform +#include <stddef.h> // size_t +#include <stdint.h> // uint8_t, uint32_t, uintptr_t +#include <string> // std::char_traits +#include <type_traits> // std::is_same_v +#include <utility> // std::move + +#include "frontend/FrontendContext.h" // FrontendContext +#include "js/Transcoding.h" // JS::TranscodeResult, JS::TranscodeBuffer, JS::TranscodeRange +#include "js/UniquePtr.h" // UniquePtr +#include "js/Utility.h" // JS::FreePolicy, js_delete +#include "vm/JSContext.h" // JSContext, ReportAllocationOverflow +#include "vm/StringType.h" // JSString + +using namespace js; + +using mozilla::Utf8Unit; + +#ifdef DEBUG +bool XDRCoderBase::validateResultCode(FrontendContext* fc, + JS::TranscodeResult code) const { + return fc->hadErrors() == bool(code == JS::TranscodeResult::Throw); +} +#endif + +template <XDRMode mode> +XDRResult XDRState<mode>::codeChars(char* chars, size_t nchars) { + return codeBytes(chars, nchars); +} + +template <XDRMode mode> +XDRResult XDRState<mode>::codeChars(Latin1Char* chars, size_t nchars) { + static_assert(sizeof(Latin1Char) == 1, + "Latin1Char must be 1 byte for nchars below to be the " + "proper count of bytes"); + static_assert(std::is_same_v<Latin1Char, unsigned char>, + "Latin1Char must be unsigned char to C++-safely reinterpret " + "the bytes generically copied below as Latin1Char"); + return codeBytes(chars, nchars); +} + +template <XDRMode mode> +XDRResult XDRState<mode>::codeChars(Utf8Unit* units, size_t count) { + if (count == 0) { + return Ok(); + } + + if (mode == XDR_ENCODE) { + uint8_t* ptr = buf->write(count); + if (!ptr) { + return fail(JS::TranscodeResult::Throw); + } + + std::transform(units, units + count, ptr, + [](const Utf8Unit& unit) { return unit.toUint8(); }); + } else { + const uint8_t* ptr = buf->read(count); + if (!ptr) { + return fail(JS::TranscodeResult::Failure_BadDecode); + } + + std::transform(ptr, ptr + count, units, + [](const uint8_t& value) { return Utf8Unit(value); }); + } + + return Ok(); +} + +template <XDRMode mode> +XDRResult XDRState<mode>::codeChars(char16_t* chars, size_t nchars) { + if (nchars == 0) { + return Ok(); + } + + size_t nbytes = nchars * sizeof(char16_t); + if (mode == XDR_ENCODE) { + uint8_t* ptr = buf->write(nbytes); + if (!ptr) { + return fail(JS::TranscodeResult::Throw); + } + + // |mozilla::NativeEndian| correctly handles writing into unaligned |ptr|. + mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr, chars, nchars); + } else { + const uint8_t* ptr = buf->read(nbytes); + if (!ptr) { + return fail(JS::TranscodeResult::Failure_BadDecode); + } + + // |mozilla::NativeEndian| correctly handles reading from unaligned |ptr|. + mozilla::NativeEndian::copyAndSwapFromLittleEndian(chars, ptr, nchars); + } + return Ok(); +} + +template <XDRMode mode, typename CharT> +static XDRResult XDRCodeCharsZ(XDRState<mode>* xdr, + XDRTranscodeString<CharT>& buffer) { + MOZ_ASSERT_IF(mode == XDR_ENCODE, !buffer.empty()); + MOZ_ASSERT_IF(mode == XDR_DECODE, buffer.empty()); + + using OwnedString = js::UniquePtr<CharT[], JS::FreePolicy>; + OwnedString owned; + + static_assert(JSString::MAX_LENGTH <= INT32_MAX, + "String length must fit in int32_t"); + + uint32_t length = 0; + CharT* chars = nullptr; + + if (mode == XDR_ENCODE) { + chars = const_cast<CharT*>(buffer.template ref<const CharT*>()); + + // Set a reasonable limit on string length. + size_t lengthSizeT = std::char_traits<CharT>::length(chars); + if (lengthSizeT > JSString::MAX_LENGTH) { + ReportAllocationOverflow(xdr->fc()); + return xdr->fail(JS::TranscodeResult::Throw); + } + length = static_cast<uint32_t>(lengthSizeT); + } + MOZ_TRY(xdr->codeUint32(&length)); + + if (mode == XDR_DECODE) { + owned = + xdr->fc()->getAllocator()->template make_pod_array<CharT>(length + 1); + if (!owned) { + return xdr->fail(JS::TranscodeResult::Throw); + } + chars = owned.get(); + } + + MOZ_TRY(xdr->codeChars(chars, length)); + if (mode == XDR_DECODE) { + // Null-terminate and transfer ownership to caller. + owned[length] = '\0'; + buffer.template construct<OwnedString>(std::move(owned)); + } + + return Ok(); +} + +template <XDRMode mode> +XDRResult XDRState<mode>::codeCharsZ(XDRTranscodeString<char>& buffer) { + return XDRCodeCharsZ(this, buffer); +} + +template <XDRMode mode> +XDRResult XDRState<mode>::codeCharsZ(XDRTranscodeString<char16_t>& buffer) { + return XDRCodeCharsZ(this, buffer); +} + +template class js::XDRState<XDR_ENCODE>; +template class js::XDRState<XDR_DECODE>; |