summaryrefslogtreecommitdiffstats
path: root/js/src/vm/Xdr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/Xdr.cpp')
-rw-r--r--js/src/vm/Xdr.cpp167
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>;