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 *
4 * Copyright 2015 Mozilla Foundation
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #ifndef wasm_serialize_h
20 #define wasm_serialize_h
21
22 #include "mozilla/Maybe.h"
23 #include "mozilla/RefPtr.h"
24
25 #include <type_traits>
26
27 #include "js/AllocPolicy.h"
28 #include "js/Vector.h"
29
30 namespace js {
31 namespace wasm {
32
33 using mozilla::MallocSizeOf;
34
35 // Factor out common serialization, cloning and about:memory size-computation
36 // functions for reuse when serializing wasm and asm.js modules.
37
WriteBytes(uint8_t * dst,const void * src,size_t nbytes)38 static inline uint8_t* WriteBytes(uint8_t* dst, const void* src,
39 size_t nbytes) {
40 if (nbytes) {
41 memcpy(dst, src, nbytes);
42 }
43 return dst + nbytes;
44 }
45
ReadBytes(const uint8_t * src,void * dst,size_t nbytes)46 static inline const uint8_t* ReadBytes(const uint8_t* src, void* dst,
47 size_t nbytes) {
48 if (nbytes) {
49 memcpy(dst, src, nbytes);
50 }
51 return src + nbytes;
52 }
53
ReadBytesChecked(const uint8_t * src,size_t * remain,void * dst,size_t nbytes)54 static inline const uint8_t* ReadBytesChecked(const uint8_t* src,
55 size_t* remain, void* dst,
56 size_t nbytes) {
57 if (*remain < nbytes) {
58 return nullptr;
59 }
60 memcpy(dst, src, nbytes);
61 *remain -= nbytes;
62 return src + nbytes;
63 }
64
65 template <class T>
WriteScalar(uint8_t * dst,T t)66 static inline uint8_t* WriteScalar(uint8_t* dst, T t) {
67 memcpy(dst, &t, sizeof(t));
68 return dst + sizeof(t);
69 }
70
71 template <class T>
ReadScalar(const uint8_t * src,T * dst)72 static inline const uint8_t* ReadScalar(const uint8_t* src, T* dst) {
73 memcpy(dst, src, sizeof(*dst));
74 return src + sizeof(*dst);
75 }
76
77 template <class T>
ReadScalarChecked(const uint8_t * src,size_t * remain,T * dst)78 static inline const uint8_t* ReadScalarChecked(const uint8_t* src,
79 size_t* remain, T* dst) {
80 if (*remain < sizeof(*dst)) {
81 return nullptr;
82 }
83 memcpy(dst, src, sizeof(*dst));
84 *remain -= sizeof(*dst);
85 return src + sizeof(*dst);
86 }
87
88 template <class T, size_t N>
SerializedVectorSize(const mozilla::Vector<T,N,SystemAllocPolicy> & vec)89 static inline size_t SerializedVectorSize(
90 const mozilla::Vector<T, N, SystemAllocPolicy>& vec) {
91 size_t size = sizeof(uint32_t);
92 for (size_t i = 0; i < vec.length(); i++) {
93 size += vec[i].serializedSize();
94 }
95 return size;
96 }
97
98 template <class T, size_t N>
SerializeVector(uint8_t * cursor,const mozilla::Vector<T,N,SystemAllocPolicy> & vec)99 static inline uint8_t* SerializeVector(
100 uint8_t* cursor, const mozilla::Vector<T, N, SystemAllocPolicy>& vec) {
101 cursor = WriteScalar<uint32_t>(cursor, vec.length());
102 for (size_t i = 0; i < vec.length(); i++) {
103 cursor = vec[i].serialize(cursor);
104 }
105 return cursor;
106 }
107
108 template <class T, size_t N>
DeserializeVector(const uint8_t * cursor,mozilla::Vector<T,N,SystemAllocPolicy> * vec)109 static inline const uint8_t* DeserializeVector(
110 const uint8_t* cursor, mozilla::Vector<T, N, SystemAllocPolicy>* vec) {
111 uint32_t length;
112 cursor = ReadScalar<uint32_t>(cursor, &length);
113 if (!vec->resize(length)) {
114 return nullptr;
115 }
116 for (size_t i = 0; i < vec->length(); i++) {
117 if (!(cursor = (*vec)[i].deserialize(cursor))) {
118 return nullptr;
119 }
120 }
121 return cursor;
122 }
123
124 template <class T, size_t N>
SizeOfVectorExcludingThis(const mozilla::Vector<T,N,SystemAllocPolicy> & vec,MallocSizeOf mallocSizeOf)125 static inline size_t SizeOfVectorExcludingThis(
126 const mozilla::Vector<T, N, SystemAllocPolicy>& vec,
127 MallocSizeOf mallocSizeOf) {
128 size_t size = vec.sizeOfExcludingThis(mallocSizeOf);
129 for (const T& t : vec) {
130 size += t.sizeOfExcludingThis(mallocSizeOf);
131 }
132 return size;
133 }
134
135 template <class T>
SerializedMaybeSize(const mozilla::Maybe<T> & maybe)136 static inline size_t SerializedMaybeSize(const mozilla::Maybe<T>& maybe) {
137 if (!maybe) {
138 return sizeof(uint8_t);
139 }
140 return sizeof(uint8_t) + maybe->serializedSize();
141 }
142
143 template <class T>
SerializeMaybe(uint8_t * cursor,const mozilla::Maybe<T> & maybe)144 static inline uint8_t* SerializeMaybe(uint8_t* cursor,
145 const mozilla::Maybe<T>& maybe) {
146 cursor = WriteScalar<uint8_t>(cursor, maybe ? 1 : 0);
147 if (maybe) {
148 cursor = maybe->serialize(cursor);
149 }
150 return cursor;
151 }
152
153 template <class T>
DeserializeMaybe(const uint8_t * cursor,mozilla::Maybe<T> * maybe)154 static inline const uint8_t* DeserializeMaybe(const uint8_t* cursor,
155 mozilla::Maybe<T>* maybe) {
156 uint8_t isSome;
157 cursor = ReadScalar<uint8_t>(cursor, &isSome);
158 if (!cursor) {
159 return nullptr;
160 }
161
162 if (isSome == 1) {
163 maybe->emplace();
164 cursor = (*maybe)->deserialize(cursor);
165 } else {
166 *maybe = mozilla::Nothing();
167 }
168 return cursor;
169 }
170
171 template <class T>
SizeOfMaybeExcludingThis(const mozilla::Maybe<T> & maybe,MallocSizeOf mallocSizeOf)172 static inline size_t SizeOfMaybeExcludingThis(const mozilla::Maybe<T>& maybe,
173 MallocSizeOf mallocSizeOf) {
174 return maybe ? maybe->sizeOfExcludingThis(mallocSizeOf) : 0;
175 }
176
177 template <class T, size_t N>
SerializedPodVectorSize(const mozilla::Vector<T,N,SystemAllocPolicy> & vec)178 static inline size_t SerializedPodVectorSize(
179 const mozilla::Vector<T, N, SystemAllocPolicy>& vec) {
180 return sizeof(uint32_t) + vec.length() * sizeof(T);
181 }
182
183 template <class T, size_t N>
SerializePodVector(uint8_t * cursor,const mozilla::Vector<T,N,SystemAllocPolicy> & vec)184 static inline uint8_t* SerializePodVector(
185 uint8_t* cursor, const mozilla::Vector<T, N, SystemAllocPolicy>& vec) {
186 // This binary format must not change without taking into consideration the
187 // constraints in Assumptions::serialize.
188
189 cursor = WriteScalar<uint32_t>(cursor, vec.length());
190 cursor = WriteBytes(cursor, vec.begin(), vec.length() * sizeof(T));
191 return cursor;
192 }
193
194 template <class T, size_t N>
DeserializePodVector(const uint8_t * cursor,mozilla::Vector<T,N,SystemAllocPolicy> * vec)195 static inline const uint8_t* DeserializePodVector(
196 const uint8_t* cursor, mozilla::Vector<T, N, SystemAllocPolicy>* vec) {
197 uint32_t length;
198 cursor = ReadScalar<uint32_t>(cursor, &length);
199 if (!vec->initLengthUninitialized(length)) {
200 return nullptr;
201 }
202 cursor = ReadBytes(cursor, vec->begin(), length * sizeof(T));
203 return cursor;
204 }
205
206 template <class T, size_t N>
DeserializePodVectorChecked(const uint8_t * cursor,size_t * remain,mozilla::Vector<T,N,SystemAllocPolicy> * vec)207 static inline const uint8_t* DeserializePodVectorChecked(
208 const uint8_t* cursor, size_t* remain,
209 mozilla::Vector<T, N, SystemAllocPolicy>* vec) {
210 uint32_t length;
211 cursor = ReadScalarChecked<uint32_t>(cursor, remain, &length);
212 if (!cursor || !vec->initLengthUninitialized(length)) {
213 return nullptr;
214 }
215 cursor = ReadBytesChecked(cursor, remain, vec->begin(), length * sizeof(T));
216 return cursor;
217 }
218
219 // To call Vector::shrinkStorageToFit , a type must specialize mozilla::IsPod
220 // which is pretty verbose to do within js::wasm, so factor that process out
221 // into a macro.
222
223 #define WASM_DECLARE_POD_VECTOR(Type, VectorName) \
224 } \
225 } \
226 namespace mozilla { \
227 template <> \
228 struct IsPod<js::wasm::Type> : std::true_type {}; \
229 } \
230 namespace js { \
231 namespace wasm { \
232 typedef Vector<Type, 0, SystemAllocPolicy> VectorName;
233
234 // A wasm Module and everything it contains must support serialization and
235 // deserialization. Some data can be simply copied as raw bytes and,
236 // as a convention, is stored in an inline CacheablePod struct. Everything else
237 // should implement the below methods which are called recusively by the
238 // containing Module.
239
240 #define WASM_DECLARE_SERIALIZABLE(Type) \
241 size_t serializedSize() const; \
242 uint8_t* serialize(uint8_t* cursor) const; \
243 const uint8_t* deserialize(const uint8_t* cursor); \
244 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
245
246 template <class T>
247 struct SerializableRefPtr : RefPtr<T> {
248 using RefPtr<T>::operator=;
249
250 SerializableRefPtr() = default;
251
252 template <class U>
SerializableRefPtrSerializableRefPtr253 MOZ_IMPLICIT SerializableRefPtr(U&& u) : RefPtr<T>(std::forward<U>(u)) {}
254
255 WASM_DECLARE_SERIALIZABLE(SerializableRefPtr)
256 };
257
258 template <class T>
serializedSize()259 inline size_t SerializableRefPtr<T>::serializedSize() const {
260 return (*this)->serializedSize();
261 }
262
263 template <class T>
serialize(uint8_t * cursor)264 inline uint8_t* SerializableRefPtr<T>::serialize(uint8_t* cursor) const {
265 return (*this)->serialize(cursor);
266 }
267
268 template <class T>
deserialize(const uint8_t * cursor)269 inline const uint8_t* SerializableRefPtr<T>::deserialize(
270 const uint8_t* cursor) {
271 auto* t = js_new<std::remove_const_t<T>>();
272 *this = t;
273 return t->deserialize(cursor);
274 }
275
276 template <class T>
sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)277 inline size_t SerializableRefPtr<T>::sizeOfExcludingThis(
278 mozilla::MallocSizeOf mallocSizeOf) const {
279 return (*this)->sizeOfExcludingThis(mallocSizeOf);
280 }
281
282 } // namespace wasm
283 } // namespace js
284
285 #endif // wasm_serialize_h
286