StencilXdr.cpp (52237B)
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/. */ 6 7 #include "frontend/StencilXdr.h" // StencilXDR 8 9 #include "mozilla/ArrayUtils.h" // mozilla::ArrayEqual 10 #include "mozilla/OperatorNewExtensions.h" // mozilla::KnownNotNull 11 #include "mozilla/RefPtr.h" // RefPtr 12 #include "mozilla/ScopeExit.h" // mozilla::MakeScopeExit 13 #include "mozilla/Try.h" // MOZ_TRY 14 15 #include <stddef.h> // size_t 16 #include <stdint.h> // uint8_t, uint16_t, uint32_t 17 #include <type_traits> // std::has_unique_object_representations 18 #include <utility> // std::forward 19 20 #include "ds/LifoAlloc.h" // LifoAlloc 21 #include "frontend/CompilationStencil.h" // CompilationStencil, ExtensibleCompilationStencil 22 #include "frontend/FrontendContext.h" // FrontendContext, AutoReportFrontendContext 23 #include "frontend/ScriptIndex.h" // ScriptIndex 24 #include "js/CompileOptions.h" // JS::ReadOnlyDecodeOptions 25 #include "js/experimental/JSStencil.h" // ScriptIndex 26 #include "js/Transcoding.h" // JS::TranscodeBuffer, JS::TranscodeRange, JS::TranscodeResult 27 #include "vm/JSScript.h" // ScriptSource 28 #include "vm/Scope.h" // SizeOfParserScopeData 29 #include "vm/StencilEnums.h" // js::ImmutableScriptFlagsEnum 30 31 using namespace js; 32 using namespace js::frontend; 33 34 using mozilla::Utf8Unit; 35 36 template <typename NameType> 37 struct CanEncodeNameType { 38 static constexpr bool value = false; 39 }; 40 41 template <> 42 struct CanEncodeNameType<TaggedParserAtomIndex> { 43 static constexpr bool value = true; 44 }; 45 46 template <XDRMode mode, typename T, size_t N, class AP> 47 static XDRResult XDRVectorUninitialized(XDRState<mode>* xdr, 48 Vector<T, N, AP>& vec, 49 uint32_t& length) { 50 if (mode == XDR_ENCODE) { 51 MOZ_ASSERT(vec.length() <= UINT32_MAX); 52 length = vec.length(); 53 } 54 55 MOZ_TRY(xdr->codeUint32(&length)); 56 57 if (mode == XDR_DECODE) { 58 MOZ_ASSERT(vec.empty()); 59 if (!vec.resizeUninitialized(length)) { 60 js::ReportOutOfMemory(xdr->fc()); 61 return xdr->fail(JS::TranscodeResult::Throw); 62 } 63 } 64 65 return Ok(); 66 } 67 68 template <XDRMode mode, typename T, size_t N, class AP> 69 static XDRResult XDRVectorInitialized(XDRState<mode>* xdr, 70 Vector<T, N, AP>& vec, uint32_t length) { 71 MOZ_ASSERT_IF(mode == XDR_ENCODE, length == vec.length()); 72 73 if (mode == XDR_DECODE) { 74 MOZ_ASSERT(vec.empty()); 75 if (!vec.resize(length)) { 76 js::ReportOutOfMemory(xdr->fc()); 77 return xdr->fail(JS::TranscodeResult::Throw); 78 } 79 } 80 81 return Ok(); 82 } 83 84 template <XDRMode mode, typename T, size_t N, class AP> 85 static XDRResult XDRVectorInitialized(XDRState<mode>* xdr, 86 Vector<T, N, AP>& vec) { 87 uint32_t length; 88 if (mode == XDR_ENCODE) { 89 MOZ_ASSERT(vec.length() <= UINT32_MAX); 90 length = vec.length(); 91 } 92 93 MOZ_TRY(xdr->codeUint32(&length)); 94 95 return XDRVectorInitialized(xdr, vec, length); 96 } 97 98 template <XDRMode mode, typename T, size_t N, class AP> 99 static XDRResult XDRVectorContent(XDRState<mode>* xdr, Vector<T, N, AP>& vec) { 100 static_assert(CanCopyDataToDisk<T>::value, 101 "Vector content cannot be bulk-copied to disk."); 102 103 uint32_t length; 104 MOZ_TRY(XDRVectorUninitialized(xdr, vec, length)); 105 MOZ_TRY(xdr->codeBytes(vec.begin(), sizeof(T) * length)); 106 107 return Ok(); 108 } 109 110 template <XDRMode mode, typename T> 111 static XDRResult XDRSpanInitialized(XDRState<mode>* xdr, LifoAlloc& alloc, 112 mozilla::Span<T>& span, uint32_t size) { 113 MOZ_ASSERT_IF(mode == XDR_ENCODE, size == span.size()); 114 115 if (mode == XDR_DECODE) { 116 MOZ_ASSERT(span.empty()); 117 if (size > 0) { 118 auto* p = alloc.template newArrayUninitialized<T>(size); 119 if (!p) { 120 js::ReportOutOfMemory(xdr->fc()); 121 return xdr->fail(JS::TranscodeResult::Throw); 122 } 123 span = mozilla::Span(p, size); 124 125 for (size_t i = 0; i < size; i++) { 126 new (mozilla::KnownNotNull, &span[i]) T(); 127 } 128 } 129 } 130 131 return Ok(); 132 } 133 134 template <XDRMode mode, typename T> 135 static XDRResult XDRSpanContent(XDRState<mode>* xdr, LifoAlloc& alloc, 136 mozilla::Span<T>& span, uint32_t size) { 137 static_assert(CanCopyDataToDisk<T>::value, 138 "Span cannot be bulk-copied to disk."); 139 MOZ_ASSERT_IF(mode == XDR_ENCODE, size == span.size()); 140 141 if (size) { 142 MOZ_TRY(xdr->align32()); 143 144 T* data; 145 if constexpr (mode == XDR_ENCODE) { 146 data = span.data(); 147 MOZ_TRY(xdr->codeBytes(data, sizeof(T) * size)); 148 } else { 149 const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options(); 150 if (options.borrowBuffer) { 151 MOZ_TRY(xdr->borrowedData(&data, sizeof(T) * size)); 152 } else { 153 data = alloc.template newArrayUninitialized<T>(size); 154 if (!data) { 155 js::ReportOutOfMemory(xdr->fc()); 156 return xdr->fail(JS::TranscodeResult::Throw); 157 } 158 MOZ_TRY(xdr->codeBytes(data, sizeof(T) * size)); 159 } 160 } 161 if (mode == XDR_DECODE) { 162 span = mozilla::Span(data, size); 163 } 164 } 165 166 return Ok(); 167 } 168 169 template <XDRMode mode, typename T> 170 static XDRResult XDRSpanContent(XDRState<mode>* xdr, LifoAlloc& alloc, 171 mozilla::Span<T>& span) { 172 uint32_t size; 173 if (mode == XDR_ENCODE) { 174 MOZ_ASSERT(span.size() <= UINT32_MAX); 175 size = span.size(); 176 } 177 178 MOZ_TRY(xdr->codeUint32(&size)); 179 180 return XDRSpanContent(xdr, alloc, span, size); 181 } 182 183 template <XDRMode mode> 184 /* static */ XDRResult StencilXDR::codeBigInt(XDRState<mode>* xdr, 185 LifoAlloc& alloc, 186 BigIntStencil& stencil) { 187 uint32_t size; 188 if (mode == XDR_ENCODE) { 189 size = stencil.bigInt_.match( 190 [](mozilla::Span<char16_t> source) { return source.size(); }, 191 [](int64_t) { return size_t(0); }); 192 } 193 MOZ_TRY(xdr->codeUint32(&size)); 194 195 // Zero-length size indicates inline storage for int64-sized BigInts. 196 if (size == 0) { 197 uint64_t num; 198 if (mode == XDR_ENCODE) { 199 num = static_cast<uint64_t>(stencil.bigInt_.as<int64_t>()); 200 } 201 MOZ_TRY(xdr->codeUint64(&num)); 202 if (mode == XDR_DECODE) { 203 stencil.bigInt_.as<int64_t>() = static_cast<int64_t>(num); 204 } 205 return Ok(); 206 } 207 208 return XDRSpanContent(xdr, alloc, stencil.source(), size); 209 } 210 211 template <XDRMode mode> 212 /* static */ XDRResult StencilXDR::codeObjLiteral(XDRState<mode>* xdr, 213 LifoAlloc& alloc, 214 ObjLiteralStencil& stencil) { 215 uint8_t kindAndFlags = 0; 216 217 if (mode == XDR_ENCODE) { 218 static_assert(sizeof(ObjLiteralKindAndFlags) == sizeof(uint8_t)); 219 kindAndFlags = stencil.kindAndFlags_.toRaw(); 220 } 221 MOZ_TRY(xdr->codeUint8(&kindAndFlags)); 222 if (mode == XDR_DECODE) { 223 stencil.kindAndFlags_.setRaw(kindAndFlags); 224 } 225 226 MOZ_TRY(xdr->codeUint32(&stencil.propertyCount_)); 227 228 MOZ_TRY(XDRSpanContent(xdr, alloc, stencil.code_)); 229 230 return Ok(); 231 } 232 233 template <typename ScopeT> 234 /* static */ void AssertScopeSpecificDataIsEncodable() { 235 using ScopeDataT = typename ScopeT::ParserData; 236 237 static_assert(CanEncodeNameType<typename ScopeDataT::NameType>::value); 238 static_assert(CanCopyDataToDisk<ScopeDataT>::value, 239 "ScopeData cannot be bulk-copied to disk"); 240 } 241 242 template <XDRMode mode> 243 /* static */ XDRResult StencilXDR::codeScopeData( 244 XDRState<mode>* xdr, LifoAlloc& alloc, ScopeStencil& stencil, 245 BaseParserScopeData*& baseScopeData) { 246 // WasmInstanceScope & WasmFunctionScope should not appear in stencils. 247 MOZ_ASSERT(stencil.kind_ != ScopeKind::WasmInstance); 248 MOZ_ASSERT(stencil.kind_ != ScopeKind::WasmFunction); 249 if (stencil.kind_ == ScopeKind::With) { 250 return Ok(); 251 } 252 253 MOZ_TRY(xdr->align32()); 254 255 static_assert(offsetof(BaseParserScopeData, length) == 0, 256 "length should be the first field"); 257 uint32_t length; 258 if (mode == XDR_ENCODE) { 259 length = baseScopeData->length; 260 } else { 261 MOZ_TRY(xdr->peekUint32(&length)); 262 } 263 264 AssertScopeSpecificDataIsEncodable<FunctionScope>(); 265 AssertScopeSpecificDataIsEncodable<VarScope>(); 266 AssertScopeSpecificDataIsEncodable<LexicalScope>(); 267 AssertScopeSpecificDataIsEncodable<ClassBodyScope>(); 268 AssertScopeSpecificDataIsEncodable<EvalScope>(); 269 AssertScopeSpecificDataIsEncodable<GlobalScope>(); 270 AssertScopeSpecificDataIsEncodable<ModuleScope>(); 271 272 // In both decoding and encoding, stencil.kind_ is now known, and 273 // can be assumed. This allows the encoding to write out the bytes 274 // for the specialized scope-data type without needing to encode 275 // a distinguishing prefix. 276 uint32_t totalLength = SizeOfParserScopeData(stencil.kind_, length); 277 if constexpr (mode == XDR_ENCODE) { 278 MOZ_TRY(xdr->codeBytes(baseScopeData, totalLength)); 279 } else { 280 const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options(); 281 if (options.borrowBuffer) { 282 MOZ_TRY(xdr->borrowedData(&baseScopeData, totalLength)); 283 } else { 284 baseScopeData = 285 reinterpret_cast<BaseParserScopeData*>(alloc.alloc(totalLength)); 286 if (!baseScopeData) { 287 js::ReportOutOfMemory(xdr->fc()); 288 return xdr->fail(JS::TranscodeResult::Throw); 289 } 290 MOZ_TRY(xdr->codeBytes(baseScopeData, totalLength)); 291 } 292 } 293 294 return Ok(); 295 } 296 297 template <XDRMode mode> 298 /* static */ 299 XDRResult StencilXDR::codeSharedData(XDRState<mode>* xdr, 300 RefPtr<SharedImmutableScriptData>& sisd) { 301 static_assert(frontend::CanCopyDataToDisk<ImmutableScriptData>::value, 302 "ImmutableScriptData cannot be bulk-copied to disk"); 303 static_assert(frontend::CanCopyDataToDisk<jsbytecode>::value, 304 "jsbytecode cannot be bulk-copied to disk"); 305 static_assert(frontend::CanCopyDataToDisk<SrcNote>::value, 306 "SrcNote cannot be bulk-copied to disk"); 307 static_assert(frontend::CanCopyDataToDisk<ScopeNote>::value, 308 "ScopeNote cannot be bulk-copied to disk"); 309 static_assert(frontend::CanCopyDataToDisk<TryNote>::value, 310 "TryNote cannot be bulk-copied to disk"); 311 312 uint32_t size; 313 uint32_t hash; 314 if (mode == XDR_ENCODE) { 315 if (sisd) { 316 size = sisd->immutableDataLength(); 317 hash = sisd->hash(); 318 } else { 319 size = 0; 320 hash = 0; 321 } 322 } 323 MOZ_TRY(xdr->codeUint32(&size)); 324 325 // A size of zero is used when the `sisd` is nullptr. This can occur for 326 // certain outer container modes. In this case, there is no further 327 // transcoding to do. 328 if (!size) { 329 MOZ_ASSERT(!sisd); 330 return Ok(); 331 } 332 333 MOZ_TRY(xdr->align32()); 334 static_assert(alignof(ImmutableScriptData) <= alignof(uint32_t)); 335 336 MOZ_TRY(xdr->codeUint32(&hash)); 337 338 if constexpr (mode == XDR_ENCODE) { 339 uint8_t* data = const_cast<uint8_t*>(sisd->get()->immutableData().data()); 340 MOZ_ASSERT(data == reinterpret_cast<const uint8_t*>(sisd->get()), 341 "Decode below relies on the data placement"); 342 MOZ_TRY(xdr->codeBytes(data, size)); 343 } else { 344 sisd = SharedImmutableScriptData::create(xdr->fc()); 345 if (!sisd) { 346 return xdr->fail(JS::TranscodeResult::Throw); 347 } 348 349 const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options(); 350 if (options.usePinnedBytecode) { 351 MOZ_ASSERT(options.borrowBuffer); 352 ImmutableScriptData* isd; 353 MOZ_TRY(xdr->borrowedData(&isd, size)); 354 sisd->setExternal(isd, hash); 355 } else { 356 auto isd = ImmutableScriptData::new_(xdr->fc(), size); 357 if (!isd) { 358 return xdr->fail(JS::TranscodeResult::Throw); 359 } 360 uint8_t* data = reinterpret_cast<uint8_t*>(isd.get()); 361 MOZ_TRY(xdr->codeBytes(data, size)); 362 sisd->setOwn(std::move(isd), hash); 363 } 364 365 if (!sisd->get()->validateLayout(size)) { 366 MOZ_ASSERT(false, "Bad ImmutableScriptData"); 367 return xdr->fail(JS::TranscodeResult::Failure_BadDecode); 368 } 369 } 370 371 if (mode == XDR_DECODE) { 372 if (!SharedImmutableScriptData::shareScriptData(xdr->fc(), sisd)) { 373 return xdr->fail(JS::TranscodeResult::Throw); 374 } 375 } 376 377 return Ok(); 378 } 379 380 // Called from js::XDRScript. 381 template /* static */ XDRResult StencilXDR::codeSharedData( 382 XDRState<XDR_ENCODE>* xdr, RefPtr<SharedImmutableScriptData>& sisd); 383 template /* static */ XDRResult StencilXDR::codeSharedData( 384 XDRState<XDR_DECODE>* xdr, RefPtr<SharedImmutableScriptData>& sisd); 385 386 template <XDRMode mode> 387 /* static */ XDRResult StencilXDR::codeSharedDataContainer( 388 XDRState<mode>* xdr, SharedDataContainer& sharedData) { 389 if (mode == XDR_ENCODE) { 390 if (sharedData.isBorrow()) { 391 return codeSharedDataContainer(xdr, *sharedData.asBorrow()); 392 } 393 } 394 395 enum Kind { 396 Single, 397 Vector, 398 Map, 399 }; 400 401 Kind kind; 402 if (mode == XDR_ENCODE) { 403 if (sharedData.isSingle()) { 404 kind = Kind::Single; 405 } else if (sharedData.isVector()) { 406 kind = Kind::Vector; 407 } else { 408 MOZ_ASSERT(sharedData.isMap()); 409 kind = Kind::Map; 410 } 411 } 412 MOZ_TRY(xdr->codeEnum32(&kind)); 413 414 switch (kind) { 415 case Kind::Single: { 416 RefPtr<SharedImmutableScriptData> ref; 417 if (mode == XDR_ENCODE) { 418 ref = sharedData.asSingle(); 419 } 420 MOZ_TRY(codeSharedData<mode>(xdr, ref)); 421 if (mode == XDR_DECODE) { 422 sharedData.setSingle(ref.forget()); 423 } 424 break; 425 } 426 427 case Kind::Vector: { 428 if (mode == XDR_DECODE) { 429 if (!sharedData.initVector(xdr->fc())) { 430 return xdr->fail(JS::TranscodeResult::Throw); 431 } 432 } 433 auto& vec = *sharedData.asVector(); 434 MOZ_TRY(XDRVectorInitialized(xdr, vec)); 435 for (auto& entry : vec) { 436 // NOTE: There can be nullptr, even if we don't perform syntax parsing, 437 // because of constant folding. 438 MOZ_TRY(codeSharedData<mode>(xdr, entry)); 439 } 440 break; 441 } 442 443 case Kind::Map: { 444 if (mode == XDR_DECODE) { 445 if (!sharedData.initMap(xdr->fc())) { 446 return xdr->fail(JS::TranscodeResult::Throw); 447 } 448 } 449 auto& map = *sharedData.asMap(); 450 uint32_t count; 451 if (mode == XDR_ENCODE) { 452 count = map.count(); 453 } 454 MOZ_TRY(xdr->codeUint32(&count)); 455 if (mode == XDR_DECODE) { 456 if (!map.reserve(count)) { 457 js::ReportOutOfMemory(xdr->fc()); 458 return xdr->fail(JS::TranscodeResult::Throw); 459 } 460 } 461 462 if (mode == XDR_ENCODE) { 463 for (auto iter = map.iter(); !iter.done(); iter.next()) { 464 uint32_t index = iter.get().key().index; 465 auto& data = iter.get().value(); 466 MOZ_TRY(xdr->codeUint32(&index)); 467 MOZ_TRY(codeSharedData<mode>(xdr, data)); 468 } 469 } else { 470 for (uint32_t i = 0; i < count; i++) { 471 ScriptIndex index; 472 MOZ_TRY(xdr->codeUint32(&index.index)); 473 474 RefPtr<SharedImmutableScriptData> data; 475 MOZ_TRY(codeSharedData<mode>(xdr, data)); 476 477 if (!map.putNew(index, data)) { 478 js::ReportOutOfMemory(xdr->fc()); 479 return xdr->fail(JS::TranscodeResult::Throw); 480 } 481 } 482 } 483 484 break; 485 } 486 487 default: 488 return xdr->fail(JS::TranscodeResult::Failure_BadDecode); 489 } 490 491 return Ok(); 492 } 493 494 template <XDRMode mode> 495 /* static */ XDRResult StencilXDR::codeParserAtom(XDRState<mode>* xdr, 496 LifoAlloc& alloc, 497 ParserAtom** atomp) { 498 static_assert(CanCopyDataToDisk<ParserAtom>::value, 499 "ParserAtom cannot be bulk-copied to disk."); 500 501 MOZ_TRY(xdr->align32()); 502 503 const ParserAtom* header; 504 if (mode == XDR_ENCODE) { 505 header = *atomp; 506 } else { 507 MOZ_TRY(xdr->peekData(&header)); 508 } 509 510 const uint32_t CharSize = 511 header->hasLatin1Chars() ? sizeof(JS::Latin1Char) : sizeof(char16_t); 512 uint32_t totalLength = sizeof(ParserAtom) + (CharSize * header->length()); 513 514 if constexpr (mode == XDR_ENCODE) { 515 MOZ_TRY(xdr->codeBytes(*atomp, totalLength)); 516 } else { 517 const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options(); 518 if (options.borrowBuffer) { 519 MOZ_TRY(xdr->borrowedData(atomp, totalLength)); 520 } else { 521 *atomp = reinterpret_cast<ParserAtom*>(alloc.alloc(totalLength)); 522 if (!*atomp) { 523 js::ReportOutOfMemory(xdr->fc()); 524 return xdr->fail(JS::TranscodeResult::Throw); 525 } 526 MOZ_TRY(xdr->codeBytes(*atomp, totalLength)); 527 } 528 } 529 530 return Ok(); 531 } 532 533 template <XDRMode mode> 534 static XDRResult XDRAtomCount(XDRState<mode>* xdr, uint32_t* atomCount) { 535 return xdr->codeUint32(atomCount); 536 } 537 538 template <XDRMode mode> 539 /* static */ XDRResult StencilXDR::codeParserAtomSpan( 540 XDRState<mode>* xdr, LifoAlloc& alloc, ParserAtomSpan& parserAtomData) { 541 if (mode == XDR_ENCODE) { 542 uint32_t atomVectorLength = parserAtomData.size(); 543 MOZ_TRY(XDRAtomCount(xdr, &atomVectorLength)); 544 545 uint32_t atomCount = 0; 546 for (const auto& entry : parserAtomData) { 547 if (!entry) { 548 continue; 549 } 550 if (entry->isUsedByStencil()) { 551 atomCount++; 552 } 553 } 554 MOZ_TRY(XDRAtomCount(xdr, &atomCount)); 555 556 for (uint32_t i = 0; i < atomVectorLength; i++) { 557 auto& entry = parserAtomData[i]; 558 if (!entry) { 559 continue; 560 } 561 if (entry->isUsedByStencil()) { 562 MOZ_TRY(xdr->codeUint32(&i)); 563 MOZ_TRY(codeParserAtom(xdr, alloc, &entry)); 564 } 565 } 566 567 return Ok(); 568 } 569 570 uint32_t atomVectorLength; 571 MOZ_TRY(XDRAtomCount(xdr, &atomVectorLength)); 572 573 frontend::ParserAtomSpanBuilder builder(parserAtomData); 574 if (!builder.allocate(xdr->fc(), alloc, atomVectorLength)) { 575 return xdr->fail(JS::TranscodeResult::Throw); 576 } 577 578 uint32_t atomCount; 579 MOZ_TRY(XDRAtomCount(xdr, &atomCount)); 580 581 for (uint32_t i = 0; i < atomCount; i++) { 582 frontend::ParserAtom* entry = nullptr; 583 uint32_t index; 584 MOZ_TRY(xdr->codeUint32(&index)); 585 MOZ_TRY(codeParserAtom(xdr, alloc, &entry)); 586 if (mode == XDR_DECODE) { 587 if (index >= atomVectorLength) { 588 return xdr->fail(JS::TranscodeResult::Failure_BadDecode); 589 } 590 } 591 builder.set(frontend::ParserAtomIndex(index), entry); 592 } 593 594 return Ok(); 595 } 596 597 template <XDRMode mode> 598 /* static */ XDRResult StencilXDR::codeModuleRequest( 599 XDRState<mode>* xdr, StencilModuleRequest& stencil) { 600 MOZ_TRY(xdr->codeUint32(stencil.specifier.rawDataRef())); 601 MOZ_TRY(xdr->codeUint32(stencil.firstUnsupportedAttributeKey.rawDataRef())); 602 MOZ_TRY(XDRVectorContent(xdr, stencil.attributes)); 603 604 return Ok(); 605 } 606 607 template <XDRMode mode> 608 /* static */ XDRResult StencilXDR::codeModuleRequestVector( 609 XDRState<mode>* xdr, StencilModuleMetadata::RequestVector& vector) { 610 MOZ_TRY(XDRVectorInitialized(xdr, vector)); 611 612 for (auto& entry : vector) { 613 MOZ_TRY(codeModuleRequest<mode>(xdr, entry)); 614 } 615 616 return Ok(); 617 } 618 619 template <XDRMode mode> 620 /* static */ XDRResult StencilXDR::codeModuleEntry( 621 XDRState<mode>* xdr, StencilModuleEntry& stencil) { 622 MOZ_TRY(xdr->codeUint32(&stencil.moduleRequest)); 623 MOZ_TRY(xdr->codeUint32(stencil.localName.rawDataRef())); 624 MOZ_TRY(xdr->codeUint32(stencil.importName.rawDataRef())); 625 MOZ_TRY(xdr->codeUint32(stencil.exportName.rawDataRef())); 626 MOZ_TRY(xdr->codeUint32(&stencil.lineno)); 627 MOZ_TRY(xdr->codeUint32(stencil.column.addressOfValueForTranscode())); 628 629 return Ok(); 630 } 631 632 template <XDRMode mode> 633 /* static */ XDRResult StencilXDR::codeModuleEntryVector( 634 XDRState<mode>* xdr, StencilModuleMetadata::EntryVector& vector) { 635 MOZ_TRY(XDRVectorInitialized(xdr, vector)); 636 637 for (auto& entry : vector) { 638 MOZ_TRY(codeModuleEntry<mode>(xdr, entry)); 639 } 640 641 return Ok(); 642 } 643 644 template <XDRMode mode> 645 /* static */ XDRResult StencilXDR::codeModuleMetadata( 646 XDRState<mode>* xdr, StencilModuleMetadata& stencil) { 647 MOZ_TRY(codeModuleRequestVector(xdr, stencil.moduleRequests)); 648 MOZ_TRY(codeModuleEntryVector(xdr, stencil.requestedModules)); 649 MOZ_TRY(codeModuleEntryVector(xdr, stencil.importEntries)); 650 MOZ_TRY(codeModuleEntryVector(xdr, stencil.localExportEntries)); 651 MOZ_TRY(codeModuleEntryVector(xdr, stencil.indirectExportEntries)); 652 MOZ_TRY(codeModuleEntryVector(xdr, stencil.starExportEntries)); 653 MOZ_TRY(XDRVectorContent(xdr, stencil.functionDecls)); 654 655 uint8_t isAsync = 0; 656 if (mode == XDR_ENCODE) { 657 if (stencil.isAsync) { 658 isAsync = stencil.isAsync ? 1 : 0; 659 } 660 } 661 662 MOZ_TRY(xdr->codeUint8(&isAsync)); 663 664 if (mode == XDR_DECODE) { 665 stencil.isAsync = isAsync == 1; 666 } 667 668 return Ok(); 669 } 670 671 template <XDRMode mode> 672 XDRResult XDRCompilationStencilSpanSize( 673 XDRState<mode>* xdr, uint32_t* scriptSize, uint32_t* gcThingSize, 674 uint32_t* scopeSize, uint32_t* scriptExtraSize, uint32_t* regExpSize, 675 uint32_t* bigIntSize, uint32_t* objLiteralSize) { 676 // Compress the series of span sizes, to avoid consuming extra space for 677 // unused/small span sizes. 678 // There will be align32 shortly after this section, so try to make the 679 // padding smaller. 680 681 enum XDRSpanSizeKind { 682 // All of the size values fit in 1 byte each. The entire section takes 7 683 // bytes, and expect no padding. 684 All8Kind, 685 686 // Other cases. All of the size values fit in 4 bytes each. Expect 3 bytes 687 // padding for `sizeKind`. 688 All32Kind, 689 }; 690 691 uint8_t sizeKind = All32Kind; 692 if (mode == XDR_ENCODE) { 693 uint32_t mask = (*scriptSize) | (*gcThingSize) | (*scopeSize) | 694 (*scriptExtraSize) | (*regExpSize) | (*bigIntSize) | 695 (*objLiteralSize); 696 697 if (mask <= 0xff) { 698 sizeKind = All8Kind; 699 } 700 } 701 MOZ_TRY(xdr->codeUint8(&sizeKind)); 702 703 if (sizeKind == All32Kind) { 704 MOZ_TRY(xdr->codeUint32(scriptSize)); 705 MOZ_TRY(xdr->codeUint32(gcThingSize)); 706 MOZ_TRY(xdr->codeUint32(scopeSize)); 707 MOZ_TRY(xdr->codeUint32(scriptExtraSize)); 708 MOZ_TRY(xdr->codeUint32(regExpSize)); 709 MOZ_TRY(xdr->codeUint32(bigIntSize)); 710 MOZ_TRY(xdr->codeUint32(objLiteralSize)); 711 } else { 712 uint8_t scriptSize8 = 0; 713 uint8_t gcThingSize8 = 0; 714 uint8_t scopeSize8 = 0; 715 uint8_t scriptExtraSize8 = 0; 716 uint8_t regExpSize8 = 0; 717 uint8_t bigIntSize8 = 0; 718 uint8_t objLiteralSize8 = 0; 719 720 if (mode == XDR_ENCODE) { 721 scriptSize8 = uint8_t(*scriptSize); 722 gcThingSize8 = uint8_t(*gcThingSize); 723 scopeSize8 = uint8_t(*scopeSize); 724 scriptExtraSize8 = uint8_t(*scriptExtraSize); 725 regExpSize8 = uint8_t(*regExpSize); 726 bigIntSize8 = uint8_t(*bigIntSize); 727 objLiteralSize8 = uint8_t(*objLiteralSize); 728 } 729 730 MOZ_TRY(xdr->codeUint8(&scriptSize8)); 731 MOZ_TRY(xdr->codeUint8(&gcThingSize8)); 732 MOZ_TRY(xdr->codeUint8(&scopeSize8)); 733 MOZ_TRY(xdr->codeUint8(&scriptExtraSize8)); 734 MOZ_TRY(xdr->codeUint8(®ExpSize8)); 735 MOZ_TRY(xdr->codeUint8(&bigIntSize8)); 736 MOZ_TRY(xdr->codeUint8(&objLiteralSize8)); 737 738 if (mode == XDR_DECODE) { 739 *scriptSize = scriptSize8; 740 *gcThingSize = gcThingSize8; 741 *scopeSize = scopeSize8; 742 *scriptExtraSize = scriptExtraSize8; 743 *regExpSize = regExpSize8; 744 *bigIntSize = bigIntSize8; 745 *objLiteralSize = objLiteralSize8; 746 } 747 } 748 749 return Ok(); 750 } 751 752 // Marker between each section inside CompilationStencil. 753 // 754 // These values should meet the following requirement: 755 // * No same value (differ more than single bit flip) 756 // * Bit pattern that won't frequently appear inside other XDR data 757 // 758 // Currently they're randomly chosen prime numbers that doesn't have same 759 // byte pattern. 760 enum class SectionMarker : uint32_t { 761 ParserAtomData = 0xD9C098D3, 762 ScopeData = 0x892C25EF, 763 ScopeNames = 0x638C4FB3, 764 RegExpData = 0xB030C2AF, 765 BigIntData = 0x4B24F449, 766 ObjLiteralData = 0x9AFAAE45, 767 SharedData = 0xAAD52687, 768 GCThingData = 0x1BD8F533, 769 ScriptData = 0x840458FF, 770 ScriptExtra = 0xA90E489D, 771 ModuleMetadata = 0x94FDCE6D, 772 End = 0x16DDA135, 773 }; 774 775 template <XDRMode mode> 776 static XDRResult CodeMarker(XDRState<mode>* xdr, SectionMarker marker) { 777 return xdr->codeMarker(uint32_t(marker)); 778 } 779 780 template <XDRMode mode> 781 /* static */ XDRResult StencilXDR::codeCompilationStencil( 782 XDRState<mode>* xdr, CompilationStencil& stencil) { 783 MOZ_ASSERT(!stencil.hasAsmJS()); 784 785 if constexpr (mode == XDR_DECODE) { 786 const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options(); 787 if (options.borrowBuffer) { 788 stencil.storageType = CompilationStencil::StorageType::Borrowed; 789 } else { 790 stencil.storageType = CompilationStencil::StorageType::Owned; 791 } 792 } 793 794 MOZ_TRY(CodeMarker(xdr, SectionMarker::ParserAtomData)); 795 MOZ_TRY(codeParserAtomSpan(xdr, stencil.alloc, stencil.parserAtomData)); 796 797 uint8_t canLazilyParse = 0; 798 799 if (mode == XDR_ENCODE) { 800 canLazilyParse = stencil.canLazilyParse; 801 } 802 MOZ_TRY(xdr->codeUint8(&canLazilyParse)); 803 if (mode == XDR_DECODE) { 804 stencil.canLazilyParse = canLazilyParse; 805 } 806 807 MOZ_TRY(xdr->codeUint32(&stencil.functionKey)); 808 809 uint32_t scriptSize, gcThingSize, scopeSize, scriptExtraSize; 810 uint32_t regExpSize, bigIntSize, objLiteralSize; 811 if (mode == XDR_ENCODE) { 812 scriptSize = stencil.scriptData.size(); 813 gcThingSize = stencil.gcThingData.size(); 814 scopeSize = stencil.scopeData.size(); 815 MOZ_ASSERT(scopeSize == stencil.scopeNames.size()); 816 817 scriptExtraSize = stencil.scriptExtra.size(); 818 819 regExpSize = stencil.regExpData.size(); 820 bigIntSize = stencil.bigIntData.size(); 821 objLiteralSize = stencil.objLiteralData.size(); 822 } 823 MOZ_TRY(XDRCompilationStencilSpanSize( 824 xdr, &scriptSize, &gcThingSize, &scopeSize, &scriptExtraSize, ®ExpSize, 825 &bigIntSize, &objLiteralSize)); 826 827 // All of the vector-indexed data elements referenced by the 828 // main script tree must be materialized first. 829 830 MOZ_TRY(CodeMarker(xdr, SectionMarker::ScopeData)); 831 MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.scopeData, scopeSize)); 832 833 MOZ_TRY(CodeMarker(xdr, SectionMarker::ScopeNames)); 834 MOZ_TRY( 835 XDRSpanInitialized(xdr, stencil.alloc, stencil.scopeNames, scopeSize)); 836 MOZ_ASSERT(stencil.scopeData.size() == stencil.scopeNames.size()); 837 for (uint32_t i = 0; i < scopeSize; i++) { 838 MOZ_TRY(codeScopeData(xdr, stencil.alloc, stencil.scopeData[i], 839 stencil.scopeNames[i])); 840 } 841 842 MOZ_TRY(CodeMarker(xdr, SectionMarker::RegExpData)); 843 MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.regExpData, regExpSize)); 844 845 MOZ_TRY(CodeMarker(xdr, SectionMarker::BigIntData)); 846 MOZ_TRY( 847 XDRSpanInitialized(xdr, stencil.alloc, stencil.bigIntData, bigIntSize)); 848 for (auto& entry : stencil.bigIntData) { 849 MOZ_TRY(codeBigInt(xdr, stencil.alloc, entry)); 850 } 851 852 MOZ_TRY(CodeMarker(xdr, SectionMarker::ObjLiteralData)); 853 MOZ_TRY(XDRSpanInitialized(xdr, stencil.alloc, stencil.objLiteralData, 854 objLiteralSize)); 855 for (auto& entry : stencil.objLiteralData) { 856 MOZ_TRY(codeObjLiteral(xdr, stencil.alloc, entry)); 857 } 858 859 MOZ_TRY(CodeMarker(xdr, SectionMarker::SharedData)); 860 MOZ_TRY(codeSharedDataContainer(xdr, stencil.sharedData)); 861 862 MOZ_TRY(CodeMarker(xdr, SectionMarker::GCThingData)); 863 MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.gcThingData, gcThingSize)); 864 865 // Now serialize the vector of ScriptStencils. 866 MOZ_TRY(CodeMarker(xdr, SectionMarker::ScriptData)); 867 MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.scriptData, scriptSize)); 868 869 MOZ_TRY(CodeMarker(xdr, SectionMarker::ScriptExtra)); 870 MOZ_TRY( 871 XDRSpanContent(xdr, stencil.alloc, stencil.scriptExtra, scriptExtraSize)); 872 873 // We don't support coding non-initial CompilationStencil. 874 MOZ_ASSERT(stencil.isInitialStencil()); 875 876 if (stencil.scriptExtra[CompilationStencil::TopLevelIndex].isModule()) { 877 if (mode == XDR_DECODE) { 878 stencil.moduleMetadata = 879 xdr->fc()->getAllocator()->template new_<StencilModuleMetadata>(); 880 if (!stencil.moduleMetadata) { 881 return xdr->fail(JS::TranscodeResult::Throw); 882 } 883 } 884 885 MOZ_TRY(CodeMarker(xdr, SectionMarker::ModuleMetadata)); 886 MOZ_TRY(codeModuleMetadata(xdr, *stencil.moduleMetadata)); 887 888 // codeModuleMetadata doesn't guarantee alignment. 889 MOZ_TRY(xdr->align32()); 890 } 891 892 MOZ_TRY(CodeMarker(xdr, SectionMarker::End)); 893 894 // The result should be aligned. 895 // 896 // NOTE: 897 // If the top-level isn't a module, ScriptData/ScriptExtra sections 898 // guarantee the alignment because there should be at least 1 item, 899 // and XDRSpanContent adds alignment before span content, and the struct size 900 // should also be aligned. 901 static_assert(sizeof(ScriptStencil) % 4 == 0, 902 "size of ScriptStencil should be aligned"); 903 static_assert(sizeof(ScriptStencilExtra) % 4 == 0, 904 "size of ScriptStencilExtra should be aligned"); 905 MOZ_RELEASE_ASSERT(xdr->isAligned32()); 906 907 return Ok(); 908 } 909 910 template <typename Unit> 911 struct UnretrievableSourceDecoder { 912 XDRState<XDR_DECODE>* const xdr_; 913 ScriptSource* const scriptSource_; 914 const uint32_t uncompressedLength_; 915 916 public: 917 UnretrievableSourceDecoder(XDRState<XDR_DECODE>* xdr, 918 ScriptSource* scriptSource, 919 uint32_t uncompressedLength) 920 : xdr_(xdr), 921 scriptSource_(scriptSource), 922 uncompressedLength_(uncompressedLength) {} 923 924 XDRResult decode() { 925 auto sourceUnits = xdr_->fc()->getAllocator()->make_pod_array<Unit>( 926 std::max<size_t>(uncompressedLength_, 1)); 927 if (!sourceUnits) { 928 return xdr_->fail(JS::TranscodeResult::Throw); 929 } 930 931 MOZ_TRY(xdr_->codeChars(sourceUnits.get(), uncompressedLength_)); 932 933 if (!scriptSource_->initializeUnretrievableUncompressedSource( 934 xdr_->fc(), std::move(sourceUnits), uncompressedLength_)) { 935 return xdr_->fail(JS::TranscodeResult::Throw); 936 } 937 938 return Ok(); 939 } 940 }; 941 942 template <> 943 XDRResult StencilXDR::codeSourceUnretrievableUncompressed<XDR_DECODE>( 944 XDRState<XDR_DECODE>* xdr, ScriptSource* ss, uint8_t sourceCharSize, 945 uint32_t uncompressedLength) { 946 MOZ_ASSERT(sourceCharSize == 1 || sourceCharSize == 2); 947 948 if (sourceCharSize == 1) { 949 UnretrievableSourceDecoder<Utf8Unit> decoder(xdr, ss, uncompressedLength); 950 return decoder.decode(); 951 } 952 953 UnretrievableSourceDecoder<char16_t> decoder(xdr, ss, uncompressedLength); 954 return decoder.decode(); 955 } 956 957 template <typename Unit> 958 struct UnretrievableSourceEncoder { 959 XDRState<XDR_ENCODE>* const xdr_; 960 ScriptSource* const source_; 961 const uint32_t uncompressedLength_; 962 963 UnretrievableSourceEncoder(XDRState<XDR_ENCODE>* xdr, ScriptSource* source, 964 uint32_t uncompressedLength) 965 : xdr_(xdr), source_(source), uncompressedLength_(uncompressedLength) {} 966 967 XDRResult encode() { 968 Unit* sourceUnits = 969 const_cast<Unit*>(source_->uncompressedData<Unit>()->units()); 970 971 return xdr_->codeChars(sourceUnits, uncompressedLength_); 972 } 973 }; 974 975 template <> 976 /* static */ 977 XDRResult StencilXDR::codeSourceUnretrievableUncompressed<XDR_ENCODE>( 978 XDRState<XDR_ENCODE>* xdr, ScriptSource* ss, uint8_t sourceCharSize, 979 uint32_t uncompressedLength) { 980 MOZ_ASSERT(sourceCharSize == 1 || sourceCharSize == 2); 981 982 if (sourceCharSize == 1) { 983 UnretrievableSourceEncoder<Utf8Unit> encoder(xdr, ss, uncompressedLength); 984 return encoder.encode(); 985 } 986 987 UnretrievableSourceEncoder<char16_t> encoder(xdr, ss, uncompressedLength); 988 return encoder.encode(); 989 } 990 991 template <typename Unit, XDRMode mode> 992 /* static */ 993 XDRResult StencilXDR::codeSourceUncompressedData(XDRState<mode>* const xdr, 994 ScriptSource* const ss) { 995 static_assert( 996 std::is_same_v<Unit, Utf8Unit> || std::is_same_v<Unit, char16_t>, 997 "should handle UTF-8 and UTF-16"); 998 999 if (mode == XDR_ENCODE) { 1000 MOZ_ASSERT(ss->isUncompressed<Unit>()); 1001 } else { 1002 MOZ_ASSERT(ss->data.is<ScriptSource::Missing>()); 1003 } 1004 1005 uint32_t uncompressedLength; 1006 if (mode == XDR_ENCODE) { 1007 uncompressedLength = ss->uncompressedData<Unit>()->length(); 1008 } 1009 MOZ_TRY(xdr->codeUint32(&uncompressedLength)); 1010 1011 return codeSourceUnretrievableUncompressed(xdr, ss, sizeof(Unit), 1012 uncompressedLength); 1013 } 1014 1015 template <typename Unit, XDRMode mode> 1016 /* static */ 1017 XDRResult StencilXDR::codeSourceCompressedData(XDRState<mode>* const xdr, 1018 ScriptSource* const ss) { 1019 static_assert( 1020 std::is_same_v<Unit, Utf8Unit> || std::is_same_v<Unit, char16_t>, 1021 "should handle UTF-8 and UTF-16"); 1022 1023 if (mode == XDR_ENCODE) { 1024 MOZ_ASSERT(ss->isCompressed<Unit>()); 1025 } else { 1026 MOZ_ASSERT(ss->data.is<ScriptSource::Missing>()); 1027 } 1028 1029 uint32_t uncompressedLength; 1030 if (mode == XDR_ENCODE) { 1031 uncompressedLength = 1032 ss->data.as<ScriptSource::Compressed<Unit, SourceRetrievable::No>>() 1033 .uncompressedLength; 1034 } 1035 MOZ_TRY(xdr->codeUint32(&uncompressedLength)); 1036 1037 uint32_t compressedLength; 1038 if (mode == XDR_ENCODE) { 1039 compressedLength = 1040 ss->data.as<ScriptSource::Compressed<Unit, SourceRetrievable::No>>() 1041 .raw.length(); 1042 } 1043 MOZ_TRY(xdr->codeUint32(&compressedLength)); 1044 1045 if (mode == XDR_DECODE) { 1046 // Compressed data is always single-byte chars. 1047 auto bytes = xdr->fc()->getAllocator()->template make_pod_array<char>( 1048 compressedLength); 1049 if (!bytes) { 1050 return xdr->fail(JS::TranscodeResult::Throw); 1051 } 1052 MOZ_TRY(xdr->codeBytes(bytes.get(), compressedLength)); 1053 1054 if (!ss->initializeWithUnretrievableCompressedSource<Unit>( 1055 xdr->fc(), std::move(bytes), compressedLength, 1056 uncompressedLength)) { 1057 return xdr->fail(JS::TranscodeResult::Throw); 1058 } 1059 } else { 1060 void* bytes = const_cast<char*>(ss->compressedData<Unit>()->raw.chars()); 1061 MOZ_TRY(xdr->codeBytes(bytes, compressedLength)); 1062 } 1063 1064 return Ok(); 1065 } 1066 1067 template <typename Unit, 1068 template <typename U, SourceRetrievable CanRetrieve> class Data, 1069 XDRMode mode> 1070 /* static */ 1071 void StencilXDR::codeSourceRetrievable(ScriptSource* const ss) { 1072 static_assert( 1073 std::is_same_v<Unit, Utf8Unit> || std::is_same_v<Unit, char16_t>, 1074 "should handle UTF-8 and UTF-16"); 1075 1076 if (mode == XDR_ENCODE) { 1077 MOZ_ASSERT((ss->data.is<Data<Unit, SourceRetrievable::Yes>>())); 1078 } else { 1079 MOZ_ASSERT(ss->data.is<ScriptSource::Missing>()); 1080 ss->data = ScriptSource::SourceType(ScriptSource::Retrievable<Unit>()); 1081 } 1082 } 1083 1084 template <typename Unit, XDRMode mode> 1085 /* static */ 1086 void StencilXDR::codeSourceRetrievableData(ScriptSource* ss) { 1087 // There's nothing to code for retrievable data. Just be sure to set 1088 // retrievable data when decoding. 1089 if (mode == XDR_ENCODE) { 1090 MOZ_ASSERT(ss->data.is<ScriptSource::Retrievable<Unit>>()); 1091 } else { 1092 MOZ_ASSERT(ss->data.is<ScriptSource::Missing>()); 1093 ss->data = ScriptSource::SourceType(ScriptSource::Retrievable<Unit>()); 1094 } 1095 } 1096 1097 template <XDRMode mode> 1098 /* static */ 1099 XDRResult StencilXDR::codeSourceData(XDRState<mode>* const xdr, 1100 ScriptSource* const ss) { 1101 // The order here corresponds to the type order in |ScriptSource::SourceType| 1102 // so number->internal Variant tag is a no-op. 1103 enum class DataType { 1104 CompressedUtf8Retrievable, 1105 UncompressedUtf8Retrievable, 1106 CompressedUtf8NotRetrievable, 1107 UncompressedUtf8NotRetrievable, 1108 CompressedUtf16Retrievable, 1109 UncompressedUtf16Retrievable, 1110 CompressedUtf16NotRetrievable, 1111 UncompressedUtf16NotRetrievable, 1112 RetrievableUtf8, 1113 RetrievableUtf16, 1114 Missing, 1115 }; 1116 1117 DataType tag; 1118 { 1119 // This is terrible, but we can't do better. When |mode == XDR_DECODE| we 1120 // don't have a |ScriptSource::data| |Variant| to match -- the entire XDR 1121 // idiom for tagged unions depends on coding a tag-number, then the 1122 // corresponding tagged data. So we must manually define a tag-enum, code 1123 // it, then switch on it (and ignore the |Variant::match| API). 1124 class XDRDataTag { 1125 public: 1126 DataType operator()( 1127 const ScriptSource::Compressed<Utf8Unit, SourceRetrievable::Yes>&) { 1128 return DataType::CompressedUtf8Retrievable; 1129 } 1130 DataType operator()( 1131 const ScriptSource::Uncompressed<Utf8Unit, SourceRetrievable::Yes>&) { 1132 return DataType::UncompressedUtf8Retrievable; 1133 } 1134 DataType operator()( 1135 const ScriptSource::Compressed<Utf8Unit, SourceRetrievable::No>&) { 1136 return DataType::CompressedUtf8NotRetrievable; 1137 } 1138 DataType operator()( 1139 const ScriptSource::Uncompressed<Utf8Unit, SourceRetrievable::No>&) { 1140 return DataType::UncompressedUtf8NotRetrievable; 1141 } 1142 DataType operator()( 1143 const ScriptSource::Compressed<char16_t, SourceRetrievable::Yes>&) { 1144 return DataType::CompressedUtf16Retrievable; 1145 } 1146 DataType operator()( 1147 const ScriptSource::Uncompressed<char16_t, SourceRetrievable::Yes>&) { 1148 return DataType::UncompressedUtf16Retrievable; 1149 } 1150 DataType operator()( 1151 const ScriptSource::Compressed<char16_t, SourceRetrievable::No>&) { 1152 return DataType::CompressedUtf16NotRetrievable; 1153 } 1154 DataType operator()( 1155 const ScriptSource::Uncompressed<char16_t, SourceRetrievable::No>&) { 1156 return DataType::UncompressedUtf16NotRetrievable; 1157 } 1158 DataType operator()(const ScriptSource::Retrievable<Utf8Unit>&) { 1159 return DataType::RetrievableUtf8; 1160 } 1161 DataType operator()(const ScriptSource::Retrievable<char16_t>&) { 1162 return DataType::RetrievableUtf16; 1163 } 1164 DataType operator()(const ScriptSource::Missing&) { 1165 return DataType::Missing; 1166 } 1167 }; 1168 1169 uint8_t type; 1170 if (mode == XDR_ENCODE) { 1171 type = static_cast<uint8_t>(ss->data.match(XDRDataTag())); 1172 } 1173 MOZ_TRY(xdr->codeUint8(&type)); 1174 1175 if (type > static_cast<uint8_t>(DataType::Missing)) { 1176 // Fail in debug, but only soft-fail in release, if the type is invalid. 1177 MOZ_ASSERT_UNREACHABLE("bad tag"); 1178 return xdr->fail(JS::TranscodeResult::Failure_BadDecode); 1179 } 1180 1181 tag = static_cast<DataType>(type); 1182 } 1183 1184 switch (tag) { 1185 case DataType::CompressedUtf8Retrievable: 1186 codeSourceRetrievable<Utf8Unit, ScriptSource::Compressed, mode>(ss); 1187 return Ok(); 1188 1189 case DataType::CompressedUtf8NotRetrievable: 1190 return codeSourceCompressedData<Utf8Unit>(xdr, ss); 1191 1192 case DataType::UncompressedUtf8Retrievable: 1193 codeSourceRetrievable<Utf8Unit, ScriptSource::Uncompressed, mode>(ss); 1194 return Ok(); 1195 1196 case DataType::UncompressedUtf8NotRetrievable: 1197 return codeSourceUncompressedData<Utf8Unit>(xdr, ss); 1198 1199 case DataType::CompressedUtf16Retrievable: 1200 codeSourceRetrievable<char16_t, ScriptSource::Compressed, mode>(ss); 1201 return Ok(); 1202 1203 case DataType::CompressedUtf16NotRetrievable: 1204 return codeSourceCompressedData<char16_t>(xdr, ss); 1205 1206 case DataType::UncompressedUtf16Retrievable: 1207 codeSourceRetrievable<char16_t, ScriptSource::Uncompressed, mode>(ss); 1208 return Ok(); 1209 1210 case DataType::UncompressedUtf16NotRetrievable: 1211 return codeSourceUncompressedData<char16_t>(xdr, ss); 1212 1213 case DataType::Missing: { 1214 MOZ_ASSERT(ss->data.is<ScriptSource::Missing>(), 1215 "ScriptSource::data is initialized as missing, so neither " 1216 "encoding nor decoding has to change anything"); 1217 1218 // There's no data to XDR for missing source. 1219 break; 1220 } 1221 1222 case DataType::RetrievableUtf8: 1223 codeSourceRetrievableData<Utf8Unit, mode>(ss); 1224 return Ok(); 1225 1226 case DataType::RetrievableUtf16: 1227 codeSourceRetrievableData<char16_t, mode>(ss); 1228 return Ok(); 1229 } 1230 1231 // The range-check on |type| far above ought ensure the above |switch| is 1232 // exhaustive and all cases will return, but not all compilers understand 1233 // this. Make the Missing case break to here so control obviously never flows 1234 // off the end. 1235 MOZ_ASSERT(tag == DataType::Missing); 1236 return Ok(); 1237 } 1238 1239 template <XDRMode mode> 1240 /* static */ 1241 XDRResult StencilXDR::codeSource(XDRState<mode>* xdr, 1242 const JS::ReadOnlyDecodeOptions* maybeOptions, 1243 RefPtr<ScriptSource>& source) { 1244 FrontendContext* fc = xdr->fc(); 1245 1246 if (mode == XDR_DECODE) { 1247 // Allocate a new ScriptSource and root it with the holder. 1248 source = do_AddRef(fc->getAllocator()->new_<ScriptSource>()); 1249 if (!source) { 1250 return xdr->fail(JS::TranscodeResult::Throw); 1251 } 1252 } 1253 1254 static constexpr uint8_t HasFilename = 1 << 0; 1255 static constexpr uint8_t HasDisplayURL = 1 << 1; 1256 static constexpr uint8_t HasSourceMapURL = 1 << 2; 1257 static constexpr uint8_t MutedErrors = 1 << 3; 1258 1259 uint8_t flags = 0; 1260 if (mode == XDR_ENCODE) { 1261 if (source->filename_) { 1262 flags |= HasFilename; 1263 } 1264 if (source->hasDisplayURL()) { 1265 flags |= HasDisplayURL; 1266 } 1267 if (source->hasSourceMapURL()) { 1268 flags |= HasSourceMapURL; 1269 } 1270 if (source->mutedErrors()) { 1271 flags |= MutedErrors; 1272 } 1273 } 1274 1275 MOZ_TRY(xdr->codeUint8(&flags)); 1276 1277 if (flags & HasFilename) { 1278 XDRTranscodeString<char> chars; 1279 1280 if (mode == XDR_ENCODE) { 1281 chars.construct<const char*>(source->filename()); 1282 } 1283 MOZ_TRY(xdr->codeCharsZ(chars)); 1284 if (mode == XDR_DECODE) { 1285 if (!source->setFilename(fc, std::move(chars.ref<UniqueChars>()))) { 1286 return xdr->fail(JS::TranscodeResult::Throw); 1287 } 1288 } 1289 } 1290 1291 if (flags & HasDisplayURL) { 1292 XDRTranscodeString<char16_t> chars; 1293 1294 if (mode == XDR_ENCODE) { 1295 chars.construct<const char16_t*>(source->displayURL()); 1296 } 1297 MOZ_TRY(xdr->codeCharsZ(chars)); 1298 if (mode == XDR_DECODE) { 1299 if (!source->setDisplayURL(fc, 1300 std::move(chars.ref<UniqueTwoByteChars>()))) { 1301 return xdr->fail(JS::TranscodeResult::Throw); 1302 } 1303 } 1304 } 1305 1306 if (flags & HasSourceMapURL) { 1307 XDRTranscodeString<char16_t> chars; 1308 1309 if (mode == XDR_ENCODE) { 1310 chars.construct<const char16_t*>(source->sourceMapURL()); 1311 } 1312 MOZ_TRY(xdr->codeCharsZ(chars)); 1313 if (mode == XDR_DECODE) { 1314 if (!source->setSourceMapURL( 1315 fc, std::move(chars.ref<UniqueTwoByteChars>()))) { 1316 return xdr->fail(JS::TranscodeResult::Throw); 1317 } 1318 } 1319 } 1320 1321 MOZ_ASSERT(source->parameterListEnd_ == 0); 1322 1323 if (flags & MutedErrors) { 1324 if (mode == XDR_DECODE) { 1325 source->mutedErrors_ = true; 1326 } 1327 } 1328 1329 MOZ_TRY(xdr->codeUint32(&source->startLine_)); 1330 MOZ_TRY(xdr->codeUint32(source->startColumn_.addressOfValueForTranscode())); 1331 1332 // The introduction info doesn't persist across encode/decode. 1333 if (mode == XDR_DECODE) { 1334 source->introductionType_ = maybeOptions->introductionType; 1335 source->setIntroductionOffset(maybeOptions->introductionOffset); 1336 if (maybeOptions->introducerFilename()) { 1337 if (!source->setIntroducerFilename( 1338 fc, maybeOptions->introducerFilename().c_str())) { 1339 return xdr->fail(JS::TranscodeResult::Throw); 1340 } 1341 } 1342 } 1343 1344 MOZ_TRY(codeSourceData(xdr, source.get())); 1345 1346 return Ok(); 1347 } 1348 1349 template /* static */ 1350 XDRResult 1351 StencilXDR::codeSource(XDRState<XDR_ENCODE>* xdr, 1352 const JS::ReadOnlyDecodeOptions* maybeOptions, 1353 RefPtr<ScriptSource>& holder); 1354 template /* static */ 1355 XDRResult 1356 StencilXDR::codeSource(XDRState<XDR_DECODE>* xdr, 1357 const JS::ReadOnlyDecodeOptions* maybeOptions, 1358 RefPtr<ScriptSource>& holder); 1359 1360 JS_PUBLIC_API bool JS::GetScriptTranscodingBuildId( 1361 JS::BuildIdCharVector* buildId) { 1362 MOZ_ASSERT(buildId->empty()); 1363 MOZ_ASSERT(GetBuildId); 1364 1365 if (!GetBuildId(buildId)) { 1366 return false; 1367 } 1368 1369 // Note: the buildId returned here is also used for the bytecode cache MIME 1370 // type so use plain ASCII characters. 1371 1372 if (!buildId->reserve(buildId->length() + 4)) { 1373 return false; 1374 } 1375 1376 buildId->infallibleAppend('-'); 1377 1378 // XDR depends on pointer size and endianness. 1379 static_assert(sizeof(uintptr_t) == 4 || sizeof(uintptr_t) == 8); 1380 buildId->infallibleAppend(sizeof(uintptr_t) == 4 ? '4' : '8'); 1381 buildId->infallibleAppend(MOZ_LITTLE_ENDIAN() ? 'l' : 'b'); 1382 1383 return true; 1384 } 1385 1386 template <XDRMode mode> 1387 static XDRResult VersionCheck(XDRState<mode>* xdr) { 1388 JS::BuildIdCharVector buildId; 1389 if (!JS::GetScriptTranscodingBuildId(&buildId)) { 1390 ReportOutOfMemory(xdr->fc()); 1391 return xdr->fail(JS::TranscodeResult::Throw); 1392 } 1393 MOZ_ASSERT(!buildId.empty()); 1394 1395 uint32_t buildIdLength; 1396 if (mode == XDR_ENCODE) { 1397 buildIdLength = buildId.length(); 1398 } 1399 1400 MOZ_TRY(xdr->codeUint32(&buildIdLength)); 1401 1402 if (mode == XDR_DECODE && buildIdLength != buildId.length()) { 1403 return xdr->fail(JS::TranscodeResult::Failure_BadBuildId); 1404 } 1405 1406 if (mode == XDR_ENCODE) { 1407 MOZ_TRY(xdr->codeBytes(buildId.begin(), buildIdLength)); 1408 } else { 1409 JS::BuildIdCharVector decodedBuildId; 1410 1411 // buildIdLength is already checked against the length of current 1412 // buildId. 1413 if (!decodedBuildId.resize(buildIdLength)) { 1414 ReportOutOfMemory(xdr->fc()); 1415 return xdr->fail(JS::TranscodeResult::Throw); 1416 } 1417 1418 MOZ_TRY(xdr->codeBytes(decodedBuildId.begin(), buildIdLength)); 1419 1420 // We do not provide binary compatibility with older scripts. 1421 if (!mozilla::ArrayEqual(decodedBuildId.begin(), buildId.begin(), 1422 buildIdLength)) { 1423 return xdr->fail(JS::TranscodeResult::Failure_BadBuildId); 1424 } 1425 } 1426 1427 return Ok(); 1428 } 1429 1430 XDRResult XDRStencilEncoder::codeStencil( 1431 const RefPtr<ScriptSource>& source, 1432 const frontend::CompilationStencil& stencil) { 1433 #ifdef DEBUG 1434 auto sanityCheck = mozilla::MakeScopeExit( 1435 [&] { MOZ_ASSERT(validateResultCode(fc(), resultCode())); }); 1436 #endif 1437 1438 MOZ_TRY(frontend::StencilXDR::checkCompilationStencil(this, stencil)); 1439 1440 MOZ_TRY(VersionCheck(this)); 1441 1442 uint32_t dummy = 0; 1443 size_t lengthOffset = buf->cursor(); 1444 MOZ_TRY(codeUint32(&dummy)); 1445 size_t hashOffset = buf->cursor(); 1446 MOZ_TRY(codeUint32(&dummy)); 1447 1448 size_t contentOffset = buf->cursor(); 1449 MOZ_TRY(frontend::StencilXDR::codeSource( 1450 this, nullptr, const_cast<RefPtr<ScriptSource>&>(source))); 1451 MOZ_TRY(frontend::StencilXDR::codeCompilationStencil( 1452 this, const_cast<frontend::CompilationStencil&>(stencil))); 1453 size_t endOffset = buf->cursor(); 1454 1455 if (endOffset > UINT32_MAX) { 1456 ReportOutOfMemory(fc()); 1457 return fail(JS::TranscodeResult::Throw); 1458 } 1459 1460 uint32_t length = endOffset - contentOffset; 1461 codeUint32At(&length, lengthOffset); 1462 1463 const uint8_t* contentBegin = buf->bufferAt(contentOffset); 1464 uint32_t hash = mozilla::HashBytes(contentBegin, length); 1465 codeUint32At(&hash, hashOffset); 1466 1467 return Ok(); 1468 } 1469 1470 XDRResult XDRStencilEncoder::codeStencil( 1471 const frontend::CompilationStencil& stencil) { 1472 return codeStencil(stencil.source, stencil); 1473 } 1474 1475 static JS::TranscodeResult EncodeStencilImpl( 1476 JS::FrontendContext* fc, const frontend::CompilationStencil* initial, 1477 JS::TranscodeBuffer& buffer) { 1478 XDRStencilEncoder encoder(fc, buffer); 1479 XDRResult res = encoder.codeStencil(*initial); 1480 if (res.isErr()) { 1481 return res.unwrapErr(); 1482 } 1483 return JS::TranscodeResult::Ok; 1484 } 1485 1486 JS::TranscodeResult JS::EncodeStencil(JSContext* cx, JS::Stencil* stencil, 1487 JS::TranscodeBuffer& buffer) { 1488 AutoReportFrontendContext fc(cx); 1489 return JS::EncodeStencil(&fc, stencil, buffer); 1490 } 1491 1492 JS::TranscodeResult JS::EncodeStencil(FrontendContext* fc, JS::Stencil* stencil, 1493 JS::TranscodeBuffer& buffer) { 1494 const CompilationStencil* initial; 1495 UniquePtr<CompilationStencil> merged; 1496 if (stencil->canLazilyParse()) { 1497 merged.reset(stencil->getMerged(fc)); 1498 if (!merged) { 1499 return TranscodeResult::Throw; 1500 } 1501 initial = merged.get(); 1502 } else { 1503 initial = stencil->getInitial(); 1504 } 1505 1506 return EncodeStencilImpl(fc, initial, buffer); 1507 } 1508 1509 JS::TranscodeResult js::EncodeStencil(JSContext* cx, 1510 frontend::CompilationStencil* stencil, 1511 JS::TranscodeBuffer& buffer) { 1512 AutoReportFrontendContext fc(cx); 1513 return EncodeStencilImpl(&fc, stencil, buffer); 1514 } 1515 1516 JS::TranscodeResult js::EncodeStencil(FrontendContext* fc, 1517 frontend::CompilationStencil* stencil, 1518 JS::TranscodeBuffer& buffer) { 1519 return EncodeStencilImpl(fc, stencil, buffer); 1520 } 1521 1522 XDRResult XDRStencilDecoder::codeStencil( 1523 const JS::ReadOnlyDecodeOptions& options, 1524 frontend::CompilationStencil& stencil) { 1525 #ifdef DEBUG 1526 auto sanityCheck = mozilla::MakeScopeExit( 1527 [&] { MOZ_ASSERT(validateResultCode(fc(), resultCode())); }); 1528 #endif 1529 1530 auto resetOptions = mozilla::MakeScopeExit([&] { options_ = nullptr; }); 1531 options_ = &options; 1532 1533 MOZ_TRY(VersionCheck(this)); 1534 1535 uint32_t length; 1536 MOZ_TRY(codeUint32(&length)); 1537 1538 uint32_t hash; 1539 MOZ_TRY(codeUint32(&hash)); 1540 1541 const uint8_t* contentBegin; 1542 MOZ_TRY(peekArray(length, &contentBegin)); 1543 uint32_t actualHash = mozilla::HashBytes(contentBegin, length); 1544 1545 if (MOZ_UNLIKELY(actualHash != hash)) { 1546 return fail(JS::TranscodeResult::Failure_BadDecode); 1547 } 1548 1549 MOZ_TRY(frontend::StencilXDR::codeSource(this, &options, stencil.source)); 1550 MOZ_TRY(frontend::StencilXDR::codeCompilationStencil(this, stencil)); 1551 1552 return Ok(); 1553 } 1554 1555 JS::TranscodeResult JS::DecodeStencil(JSContext* cx, 1556 const JS::ReadOnlyDecodeOptions& options, 1557 const JS::TranscodeRange& range, 1558 JS::Stencil** stencilOut) { 1559 AutoReportFrontendContext fc(cx); 1560 return JS::DecodeStencil(&fc, options, range, stencilOut); 1561 } 1562 1563 JS::TranscodeResult JS::DecodeStencil(JS::FrontendContext* fc, 1564 const JS::ReadOnlyDecodeOptions& options, 1565 const JS::TranscodeRange& range, 1566 JS::Stencil** stencilOut) { 1567 RefPtr<CompilationStencil> stencil; 1568 JS::TranscodeResult result = 1569 js::DecodeStencil(fc, options, range, getter_AddRefs(stencil)); 1570 if (result != TranscodeResult::Ok) { 1571 return result; 1572 } 1573 1574 RefPtr stencils = 1575 fc->getAllocator()->new_<frontend::InitialStencilAndDelazifications>(); 1576 if (!stencils) { 1577 return TranscodeResult::Throw; 1578 } 1579 if (!stencils->init(fc, stencil.get())) { 1580 return TranscodeResult::Throw; 1581 } 1582 stencils.forget(stencilOut); 1583 return TranscodeResult::Ok; 1584 } 1585 1586 JS::TranscodeResult js::DecodeStencil( 1587 JS::FrontendContext* fc, const JS::ReadOnlyDecodeOptions& options, 1588 const JS::TranscodeRange& range, 1589 frontend::CompilationStencil** stencilOut) { 1590 RefPtr<ScriptSource> source = fc->getAllocator()->new_<ScriptSource>(); 1591 if (!source) { 1592 return JS::TranscodeResult::Throw; 1593 } 1594 RefPtr<CompilationStencil> stencil = 1595 fc->getAllocator()->new_<CompilationStencil>(source); 1596 if (!stencil) { 1597 return JS::TranscodeResult::Throw; 1598 } 1599 XDRStencilDecoder decoder(fc, range); 1600 XDRResult res = decoder.codeStencil(options, *stencil); 1601 if (res.isErr()) { 1602 return res.unwrapErr(); 1603 } 1604 stencil.forget(stencilOut); 1605 return JS::TranscodeResult::Ok; 1606 } 1607 1608 template /* static */ XDRResult StencilXDR::codeCompilationStencil( 1609 XDRState<XDR_ENCODE>* xdr, CompilationStencil& stencil); 1610 1611 template /* static */ XDRResult StencilXDR::codeCompilationStencil( 1612 XDRState<XDR_DECODE>* xdr, CompilationStencil& stencil); 1613 1614 /* static */ XDRResult StencilXDR::checkCompilationStencil( 1615 XDRStencilEncoder* encoder, const CompilationStencil& stencil) { 1616 if (stencil.hasAsmJS()) { 1617 return encoder->fail(JS::TranscodeResult::Failure_AsmJSNotSupported); 1618 } 1619 1620 return Ok(); 1621 } 1622 1623 /* static */ XDRResult StencilXDR::checkCompilationStencil( 1624 const ExtensibleCompilationStencil& stencil) { 1625 if (stencil.hasAsmJS()) { 1626 return mozilla::Err(JS::TranscodeResult::Failure_AsmJSNotSupported); 1627 } 1628 1629 return Ok(); 1630 }