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