Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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/. */
6 :
7 : #include "vm/Xdr.h"
8 :
9 : #include "mozilla/PodOperations.h"
10 :
11 : #include <string.h>
12 :
13 : #include "jsapi.h"
14 : #include "jscntxt.h"
15 : #include "jsscript.h"
16 :
17 : #include "vm/Debugger.h"
18 : #include "vm/EnvironmentObject.h"
19 : #include "vm/TraceLogging.h"
20 :
21 : using namespace js;
22 : using mozilla::PodEqual;
23 :
24 : template<XDRMode mode>
25 : LifoAlloc&
26 95 : XDRState<mode>::lifoAlloc() const {
27 95 : return buf.cx()->tempLifoAlloc();
28 : }
29 :
30 : template<XDRMode mode>
31 : void
32 0 : XDRState<mode>::postProcessContextErrors(JSContext* cx)
33 : {
34 0 : if (!cx->helperThread() && cx->isExceptionPending()) {
35 0 : MOZ_ASSERT(resultCode_ == JS::TranscodeResult_Ok ||
36 : resultCode_ == JS::TranscodeResult_Throw);
37 0 : resultCode_ = JS::TranscodeResult_Throw;
38 : }
39 0 : }
40 :
41 : template<XDRMode mode>
42 : bool
43 44010 : XDRState<mode>::codeChars(const Latin1Char* chars, size_t nchars)
44 : {
45 : static_assert(sizeof(Latin1Char) == sizeof(uint8_t), "Latin1Char must fit in 1 byte");
46 :
47 0 : MOZ_ASSERT(mode == XDR_ENCODE);
48 :
49 44010 : if (nchars == 0)
50 98 : return true;
51 43912 : uint8_t* ptr = buf.write(nchars);
52 43912 : if (!ptr)
53 0 : return fail(JS::TranscodeResult_Throw);
54 :
55 43912 : mozilla::PodCopy(ptr, chars, nchars);
56 43912 : return true;
57 : }
58 :
59 : template<XDRMode mode>
60 : bool
61 0 : XDRState<mode>::codeChars(char16_t* chars, size_t nchars)
62 : {
63 0 : if (nchars == 0)
64 0 : return true;
65 0 : size_t nbytes = nchars * sizeof(char16_t);
66 : if (mode == XDR_ENCODE) {
67 0 : uint8_t* ptr = buf.write(nbytes);
68 0 : if (!ptr)
69 0 : return fail(JS::TranscodeResult_Throw);
70 0 : mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr, chars, nchars);
71 : } else {
72 0 : const uint8_t* ptr = buf.read(nbytes);
73 0 : mozilla::NativeEndian::copyAndSwapFromLittleEndian(chars, ptr, nchars);
74 : }
75 0 : return true;
76 : }
77 :
78 : template<XDRMode mode>
79 : static bool
80 1432 : VersionCheck(XDRState<mode>* xdr)
81 : {
82 2864 : JS::BuildIdCharVector buildId;
83 1432 : if (!xdr->cx()->buildIdOp() || !xdr->cx()->buildIdOp()(&buildId))
84 0 : return xdr->fail(JS::TranscodeResult_Failure_BadBuildId);
85 1432 : MOZ_ASSERT(!buildId.empty());
86 :
87 : uint32_t buildIdLength;
88 : if (mode == XDR_ENCODE)
89 82 : buildIdLength = buildId.length();
90 :
91 1432 : if (!xdr->codeUint32(&buildIdLength))
92 0 : return false;
93 :
94 1432 : if (mode == XDR_DECODE && buildIdLength != buildId.length())
95 0 : return xdr->fail(JS::TranscodeResult_Failure_BadBuildId);
96 :
97 : if (mode == XDR_ENCODE) {
98 82 : if (!xdr->codeBytes(buildId.begin(), buildIdLength))
99 0 : return false;
100 : } else {
101 2700 : JS::BuildIdCharVector decodedBuildId;
102 :
103 : // buildIdLength is already checked against the length of current
104 : // buildId.
105 1350 : if (!decodedBuildId.resize(buildIdLength)) {
106 0 : ReportOutOfMemory(xdr->cx());
107 0 : return xdr->fail(JS::TranscodeResult_Throw);
108 : }
109 :
110 1350 : if (!xdr->codeBytes(decodedBuildId.begin(), buildIdLength))
111 0 : return false;
112 :
113 : // We do not provide binary compatibility with older scripts.
114 1350 : if (!PodEqual(decodedBuildId.begin(), buildId.begin(), buildIdLength))
115 0 : return xdr->fail(JS::TranscodeResult_Failure_BadBuildId);
116 : }
117 :
118 1432 : return true;
119 : }
120 :
121 : template<XDRMode mode>
122 : bool
123 1091 : XDRState<mode>::codeFunction(MutableHandleFunction funp, HandleScriptSource sourceObject)
124 : {
125 1091 : TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx());
126 : TraceLoggerTextId event =
127 1091 : mode == XDR_DECODE ? TraceLogger_DecodeFunction : TraceLogger_EncodeFunction;
128 2182 : AutoTraceLog tl(logger, event);
129 :
130 2182 : RootedScope scope(cx(), &cx()->global()->emptyGlobalScope());
131 : if (mode == XDR_DECODE) {
132 1091 : MOZ_ASSERT(!sourceObject);
133 1091 : funp.set(nullptr);
134 0 : } else if (getTreeKey(funp) != AutoXDRTree::noKey) {
135 0 : MOZ_ASSERT(sourceObject);
136 0 : scope = funp->nonLazyScript()->enclosingScope();
137 : } else {
138 0 : MOZ_ASSERT(!sourceObject);
139 0 : MOZ_ASSERT(funp->nonLazyScript()->enclosingScope()->is<GlobalScope>());
140 : }
141 :
142 1091 : if (!VersionCheck(this)) {
143 0 : postProcessContextErrors(cx());
144 0 : return false;
145 : }
146 :
147 1091 : if (!XDRInterpretedFunction(this, scope, sourceObject, funp)) {
148 0 : postProcessContextErrors(cx());
149 0 : funp.set(nullptr);
150 0 : return false;
151 : }
152 :
153 1091 : return true;
154 : }
155 :
156 : template<XDRMode mode>
157 : bool
158 341 : XDRState<mode>::codeScript(MutableHandleScript scriptp)
159 : {
160 341 : TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx());
161 : TraceLoggerTextId event =
162 341 : mode == XDR_DECODE ? TraceLogger_DecodeScript : TraceLogger_EncodeScript;
163 680 : AutoTraceLog tl(logger, event);
164 :
165 680 : AutoXDRTree scriptTree(this, getTopLevelTreeKey());
166 :
167 : if (mode == XDR_DECODE)
168 259 : scriptp.set(nullptr);
169 : else
170 82 : MOZ_ASSERT(!scriptp->enclosingScope());
171 :
172 341 : if (!VersionCheck(this)) {
173 0 : postProcessContextErrors(cx());
174 0 : return false;
175 : }
176 :
177 341 : if (!XDRScript(this, nullptr, nullptr, nullptr, scriptp)) {
178 0 : postProcessContextErrors(cx());
179 0 : scriptp.set(nullptr);
180 0 : return false;
181 : }
182 :
183 339 : return true;
184 : }
185 :
186 : template<XDRMode mode>
187 : bool
188 22496 : XDRState<mode>::codeConstValue(MutableHandleValue vp)
189 : {
190 22496 : return XDRScriptConst(this, vp);
191 : }
192 :
193 : template class js::XDRState<XDR_ENCODE>;
194 : template class js::XDRState<XDR_DECODE>;
195 :
196 13619 : AutoXDRTree::AutoXDRTree(XDRCoderBase* xdr, AutoXDRTree::Key key)
197 : : key_(key),
198 : parent_(this),
199 13619 : xdr_(xdr)
200 : {
201 13619 : if (key_ != AutoXDRTree::noKey)
202 0 : xdr->createOrReplaceSubTree(this);
203 13619 : }
204 :
205 27234 : AutoXDRTree::~AutoXDRTree()
206 : {
207 13617 : if (key_ != AutoXDRTree::noKey)
208 0 : xdr_->endSubTree();
209 13617 : }
210 :
211 : constexpr AutoXDRTree::Key AutoXDRTree::noKey;
212 : constexpr AutoXDRTree::Key AutoXDRTree::noSubTree;
213 : constexpr AutoXDRTree::Key AutoXDRTree::topLevel;
214 :
215 : AutoXDRTree::Key
216 0 : XDRIncrementalEncoder::getTopLevelTreeKey() const
217 : {
218 0 : return AutoXDRTree::topLevel;
219 : }
220 :
221 : AutoXDRTree::Key
222 0 : XDRIncrementalEncoder::getTreeKey(JSFunction* fun) const
223 : {
224 0 : if (fun->isInterpretedLazy()) {
225 : static_assert(sizeof(fun->lazyScript()->begin()) == 4 ||
226 : sizeof(fun->lazyScript()->end()) == 4,
227 : "AutoXDRTree key requires LazyScripts positions to be uint32");
228 0 : return uint64_t(fun->lazyScript()->begin()) << 32 | fun->lazyScript()->end();
229 : }
230 :
231 0 : if (fun->isInterpreted()) {
232 : static_assert(sizeof(fun->nonLazyScript()->sourceStart()) == 4 ||
233 : sizeof(fun->nonLazyScript()->sourceEnd()) == 4,
234 : "AutoXDRTree key requires JSScripts positions to be uint32");
235 0 : return uint64_t(fun->nonLazyScript()->sourceStart()) << 32 | fun->nonLazyScript()->sourceEnd();
236 : }
237 :
238 0 : return AutoXDRTree::noKey;
239 : }
240 :
241 : bool
242 0 : XDRIncrementalEncoder::init()
243 : {
244 0 : if (!tree_.init())
245 0 : return false;
246 0 : return true;
247 : }
248 :
249 : void
250 0 : XDRIncrementalEncoder::createOrReplaceSubTree(AutoXDRTree* child)
251 : {
252 0 : AutoXDRTree* parent = scope_;
253 0 : child->parent_ = parent;
254 0 : scope_ = child;
255 0 : if (oom_)
256 0 : return;
257 :
258 0 : size_t cursor = buf.cursor();
259 :
260 : // End the parent slice here, set the key to the child.
261 0 : if (parent) {
262 0 : Slice& last = node_->back();
263 0 : last.sliceLength = cursor - last.sliceBegin;
264 0 : last.child = child->key_;
265 0 : MOZ_ASSERT_IF(uint32_t(parent->key_) != 0,
266 : uint32_t(parent->key_ >> 32) <= uint32_t(child->key_ >> 32) &&
267 : uint32_t(child->key_) <= uint32_t(parent->key_));
268 : }
269 :
270 : // Create or replace the part with what is going to be encoded next.
271 0 : SlicesTree::AddPtr p = tree_.lookupForAdd(child->key_);
272 0 : SlicesNode tmp;
273 0 : if (!p) {
274 : // Create a new sub-tree node.
275 0 : if (!tree_.add(p, child->key_, mozilla::Move(tmp))) {
276 0 : oom_ = true;
277 0 : return;
278 : }
279 : } else {
280 : // Replace an exisiting sub-tree.
281 0 : p->value() = mozilla::Move(tmp);
282 : }
283 0 : node_ = &p->value();
284 :
285 : // Add content to the root of the new sub-tree,
286 : // i-e an empty slice with no children.
287 0 : if (!node_->append(Slice { cursor, 0, AutoXDRTree::noSubTree }))
288 0 : MOZ_CRASH("SlicesNode have a reserved space of 1.");
289 : }
290 :
291 : void
292 0 : XDRIncrementalEncoder::endSubTree()
293 : {
294 0 : AutoXDRTree* child = scope_;
295 0 : AutoXDRTree* parent = child->parent_;
296 0 : scope_ = parent;
297 0 : if (oom_)
298 0 : return;
299 :
300 0 : size_t cursor = buf.cursor();
301 :
302 : // End the child sub-tree.
303 0 : Slice& last = node_->back();
304 0 : last.sliceLength = cursor - last.sliceBegin;
305 0 : MOZ_ASSERT(last.child == AutoXDRTree::noSubTree);
306 :
307 : // Stop at the top-level.
308 0 : if (!parent) {
309 0 : node_ = nullptr;
310 0 : return;
311 : }
312 :
313 : // Restore the parent node.
314 0 : SlicesTree::Ptr p = tree_.lookup(parent->key_);
315 0 : node_ = &p->value();
316 :
317 : // Append the new slice in the parent node.
318 0 : if (!node_->append(Slice { cursor, 0, AutoXDRTree::noSubTree })) {
319 0 : oom_ = true;
320 0 : return;
321 : }
322 : }
323 :
324 : bool
325 0 : XDRIncrementalEncoder::linearize(JS::TranscodeBuffer& buffer)
326 : {
327 0 : if (oom_) {
328 0 : ReportOutOfMemory(cx());
329 0 : return fail(JS::TranscodeResult_Throw);
330 : }
331 :
332 : // Do not linearize while we are currently adding bytes.
333 0 : MOZ_ASSERT(scope_ == nullptr);
334 :
335 : // Visit the tree parts in a depth first order, to linearize the bits.
336 0 : Vector<SlicesNode::ConstRange> depthFirst(cx());
337 :
338 0 : SlicesTree::Ptr p = tree_.lookup(AutoXDRTree::topLevel);
339 0 : MOZ_ASSERT(p);
340 :
341 0 : if (!depthFirst.append(((const SlicesNode&) p->value()).all())) {
342 0 : ReportOutOfMemory(cx());
343 0 : return fail(JS::TranscodeResult_Throw);
344 : }
345 :
346 0 : while (!depthFirst.empty()) {
347 0 : SlicesNode::ConstRange& iter = depthFirst.back();
348 0 : Slice slice = iter.popCopyFront();
349 : // These fields have different meaning, but they should be correlated if
350 : // the tree is well formatted.
351 0 : MOZ_ASSERT_IF(slice.child == AutoXDRTree::noSubTree, iter.empty());
352 0 : if (iter.empty())
353 0 : depthFirst.popBack();
354 :
355 : // Copy the bytes associated with the current slice to the transcode
356 : // buffer which would be serialized.
357 0 : MOZ_ASSERT(slice.sliceBegin <= slices_.length());
358 0 : MOZ_ASSERT(slice.sliceBegin + slice.sliceLength <= slices_.length());
359 0 : if (!buffer.append(slices_.begin() + slice.sliceBegin, slice.sliceLength)) {
360 0 : ReportOutOfMemory(cx());
361 0 : return fail(JS::TranscodeResult_Throw);
362 : }
363 :
364 : // If we are at the end, go to back to the parent script.
365 0 : if (slice.child == AutoXDRTree::noSubTree)
366 0 : continue;
367 :
368 : // Visit the sub-parts before visiting the rest of the current slice.
369 0 : SlicesTree::Ptr p = tree_.lookup(slice.child);
370 0 : MOZ_ASSERT(p);
371 0 : if (!depthFirst.append(((const SlicesNode&) p->value()).all())) {
372 0 : ReportOutOfMemory(cx());
373 0 : return fail(JS::TranscodeResult_Throw);
374 : }
375 : }
376 :
377 0 : tree_.finish();
378 0 : slices_.clearAndFree();
379 0 : return true;
380 : }
|