/* -*- 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 // std::transform #include // size_t #include // uint8_t, uint32_t, uintptr_t #include // std::char_traits #include // std::is_same_v #include // 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 XDRResult XDRState::codeChars(char* chars, size_t nchars) { return codeBytes(chars, nchars); } template XDRResult XDRState::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 must be unsigned char to C++-safely reinterpret " "the bytes generically copied below as Latin1Char"); return codeBytes(chars, nchars); } template XDRResult XDRState::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 XDRResult XDRState::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 static XDRResult XDRCodeCharsZ(XDRState* xdr, XDRTranscodeString& buffer) { MOZ_ASSERT_IF(mode == XDR_ENCODE, !buffer.empty()); MOZ_ASSERT_IF(mode == XDR_DECODE, buffer.empty()); using OwnedString = js::UniquePtr; 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(buffer.template ref()); // Set a reasonable limit on string length. size_t lengthSizeT = std::char_traits::length(chars); if (lengthSizeT > JSString::MAX_LENGTH) { ReportAllocationOverflow(xdr->fc()); return xdr->fail(JS::TranscodeResult::Throw); } length = static_cast(lengthSizeT); } MOZ_TRY(xdr->codeUint32(&length)); if (mode == XDR_DECODE) { owned = xdr->fc()->getAllocator()->template make_pod_array(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(std::move(owned)); } return Ok(); } template XDRResult XDRState::codeCharsZ(XDRTranscodeString& buffer) { return XDRCodeCharsZ(this, buffer); } template XDRResult XDRState::codeCharsZ(XDRTranscodeString& buffer) { return XDRCodeCharsZ(this, buffer); } template class js::XDRState; template class js::XDRState;