1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF
10 #include "mozilla/EndianUtils.h" // mozilla::NativeEndian, MOZ_LITTLE_ENDIAN
11 #include "mozilla/Result.h" // mozilla::{Result, Ok, Err}, MOZ_TRY
12 #include "mozilla/Utf8.h" // mozilla::Utf8Unit
14 #include <algorithm> // std::transform
15 #include <stddef.h> // size_t
16 #include <stdint.h> // uint8_t, uint32_t, uintptr_t
17 #include <string> // std::char_traits
18 #include <type_traits> // std::is_same_v
19 #include <utility> // std::move
21 #include "frontend/FrontendContext.h" // FrontendContext
22 #include "js/Transcoding.h" // JS::TranscodeResult, JS::TranscodeBuffer, JS::TranscodeRange
23 #include "js/UniquePtr.h" // UniquePtr
24 #include "js/Utility.h" // JS::FreePolicy, js_delete
25 #include "vm/JSContext.h" // JSContext, ReportAllocationOverflow
26 #include "vm/StringType.h" // JSString
30 using mozilla::Utf8Unit
;
33 bool XDRCoderBase::validateResultCode(FrontendContext
* fc
,
34 JS::TranscodeResult code
) const {
35 return fc
->hadErrors() == bool(code
== JS::TranscodeResult::Throw
);
39 template <XDRMode mode
>
40 XDRResult XDRState
<mode
>::codeChars(char* chars
, size_t nchars
) {
41 return codeBytes(chars
, nchars
);
44 template <XDRMode mode
>
45 XDRResult XDRState
<mode
>::codeChars(Latin1Char
* chars
, size_t nchars
) {
46 static_assert(sizeof(Latin1Char
) == 1,
47 "Latin1Char must be 1 byte for nchars below to be the "
48 "proper count of bytes");
49 static_assert(std::is_same_v
<Latin1Char
, unsigned char>,
50 "Latin1Char must be unsigned char to C++-safely reinterpret "
51 "the bytes generically copied below as Latin1Char");
52 return codeBytes(chars
, nchars
);
55 template <XDRMode mode
>
56 XDRResult XDRState
<mode
>::codeChars(Utf8Unit
* units
, size_t count
) {
61 if (mode
== XDR_ENCODE
) {
62 uint8_t* ptr
= buf
->write(count
);
64 return fail(JS::TranscodeResult::Throw
);
67 std::transform(units
, units
+ count
, ptr
,
68 [](const Utf8Unit
& unit
) { return unit
.toUint8(); });
70 const uint8_t* ptr
= buf
->read(count
);
72 return fail(JS::TranscodeResult::Failure_BadDecode
);
75 std::transform(ptr
, ptr
+ count
, units
,
76 [](const uint8_t& value
) { return Utf8Unit(value
); });
82 template <XDRMode mode
>
83 XDRResult XDRState
<mode
>::codeChars(char16_t
* chars
, size_t nchars
) {
88 size_t nbytes
= nchars
* sizeof(char16_t
);
89 if (mode
== XDR_ENCODE
) {
90 uint8_t* ptr
= buf
->write(nbytes
);
92 return fail(JS::TranscodeResult::Throw
);
95 // |mozilla::NativeEndian| correctly handles writing into unaligned |ptr|.
96 mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr
, chars
, nchars
);
98 const uint8_t* ptr
= buf
->read(nbytes
);
100 return fail(JS::TranscodeResult::Failure_BadDecode
);
103 // |mozilla::NativeEndian| correctly handles reading from unaligned |ptr|.
104 mozilla::NativeEndian::copyAndSwapFromLittleEndian(chars
, ptr
, nchars
);
109 template <XDRMode mode
, typename CharT
>
110 static XDRResult
XDRCodeCharsZ(XDRState
<mode
>* xdr
,
111 XDRTranscodeString
<CharT
>& buffer
) {
112 MOZ_ASSERT_IF(mode
== XDR_ENCODE
, !buffer
.empty());
113 MOZ_ASSERT_IF(mode
== XDR_DECODE
, buffer
.empty());
115 using OwnedString
= js::UniquePtr
<CharT
[], JS::FreePolicy
>;
118 static_assert(JSString::MAX_LENGTH
<= INT32_MAX
,
119 "String length must fit in int32_t");
122 CharT
* chars
= nullptr;
124 if (mode
== XDR_ENCODE
) {
125 chars
= const_cast<CharT
*>(buffer
.template ref
<const CharT
*>());
127 // Set a reasonable limit on string length.
128 size_t lengthSizeT
= std::char_traits
<CharT
>::length(chars
);
129 if (lengthSizeT
> JSString::MAX_LENGTH
) {
130 ReportAllocationOverflow(xdr
->fc());
131 return xdr
->fail(JS::TranscodeResult::Throw
);
133 length
= static_cast<uint32_t>(lengthSizeT
);
135 MOZ_TRY(xdr
->codeUint32(&length
));
137 if (mode
== XDR_DECODE
) {
139 xdr
->fc()->getAllocator()->template make_pod_array
<CharT
>(length
+ 1);
141 return xdr
->fail(JS::TranscodeResult::Throw
);
146 MOZ_TRY(xdr
->codeChars(chars
, length
));
147 if (mode
== XDR_DECODE
) {
148 // Null-terminate and transfer ownership to caller.
149 owned
[length
] = '\0';
150 buffer
.template construct
<OwnedString
>(std::move(owned
));
156 template <XDRMode mode
>
157 XDRResult XDRState
<mode
>::codeCharsZ(XDRTranscodeString
<char>& buffer
) {
158 return XDRCodeCharsZ(this, buffer
);
161 template <XDRMode mode
>
162 XDRResult XDRState
<mode
>::codeCharsZ(XDRTranscodeString
<char16_t
>& buffer
) {
163 return XDRCodeCharsZ(this, buffer
);
166 template class js::XDRState
<XDR_ENCODE
>;
167 template class js::XDRState
<XDR_DECODE
>;