2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/func-emitter.h"
19 #include "hphp/runtime/base/array-iterator.h"
20 #include "hphp/runtime/base/coeffects-config.h"
21 #include "hphp/runtime/base/unit-cache.h"
22 #include "hphp/runtime/base/rds.h"
24 #include "hphp/runtime/ext/extension.h"
26 #include "hphp/runtime/vm/bytecode.h"
27 #include "hphp/runtime/vm/native.h"
28 #include "hphp/runtime/vm/preclass-emitter.h"
29 #include "hphp/runtime/vm/reified-generics.h"
30 #include "hphp/runtime/vm/repo-autoload-map-builder.h"
31 #include "hphp/runtime/vm/repo-file.h"
32 #include "hphp/runtime/vm/runtime.h"
34 #include "hphp/runtime/vm/jit/types.h"
36 #include "hphp/runtime/vm/verifier/cfg.h"
38 #include "hphp/system/systemlib.h"
40 #include "hphp/util/atomic-vector.h"
41 #include "hphp/util/blob-encoder.h"
42 #include "hphp/util/file.h"
43 #include "hphp/util/trace.h"
47 ///////////////////////////////////////////////////////////////////////////////
50 FuncEmitter::FuncEmitter(UnitEmitter
& ue
, int sn
, Id id
, const StringData
* n
)
60 , retUserType(nullptr)
62 , originalFilename(nullptr)
63 , memoizePropName(nullptr)
64 , memoizeGuardPropName(nullptr)
65 , memoizeSharedPropIndex(0)
67 , m_numUnnamedLocals(0)
69 , m_nextFreeIterator(0)
70 , m_ehTabSorted(false)
73 FuncEmitter::FuncEmitter(UnitEmitter
& ue
, int sn
, const StringData
* n
,
84 , retUserType(nullptr)
86 , originalFilename(nullptr)
87 , memoizePropName(nullptr)
88 , memoizeGuardPropName(nullptr)
89 , memoizeSharedPropIndex(0)
91 , m_numUnnamedLocals(0)
93 , m_nextFreeIterator(0)
94 , m_ehTabSorted(false)
97 FuncEmitter::~FuncEmitter() {
99 if (auto const p
= m_bc
.ptr()) free(p
);
101 if (m_lineTable
.isPtr()) {
102 if (auto const p
= m_lineTable
.ptr()) delete p
;
107 ///////////////////////////////////////////////////////////////////////////////
110 SourceLocTable
FuncEmitter::createSourceLocTable() const {
111 assertx(m_sourceLocTab
.size() != 0);
112 SourceLocTable locations
;
113 for (size_t i
= 0; i
< m_sourceLocTab
.size(); ++i
) {
114 Offset endOff
= i
< m_sourceLocTab
.size() - 1
115 ? m_sourceLocTab
[i
+ 1].first
117 locations
.push_back(SourceLocEntry(endOff
, m_sourceLocTab
[i
].second
));
122 void FuncEmitter::setLineTable(LineTable table
) {
123 if (m_lineTable
.isPtr()) {
124 if (auto p
= m_lineTable
.ptr()) {
125 *p
= std::move(table
);
129 m_lineTable
= Func::LineTablePtr::FromPtr(new LineTable
{std::move(table
)});
134 using SrcLoc
= std::vector
<std::pair
<Offset
, SourceLoc
>>;
137 * Create a LineTable from `srcLoc'.
139 LineTable
createLineTable(const SrcLoc
& srcLoc
, Offset bclen
) {
141 if (srcLoc
.empty()) {
145 auto prev
= srcLoc
.begin();
146 for (auto it
= prev
+ 1; it
!= srcLoc
.end(); ++it
) {
147 if (prev
->second
.line1
!= it
->second
.line1
) {
148 lines
.push_back(LineEntry(it
->first
, prev
->second
.line1
));
153 lines
.push_back(LineEntry(bclen
, prev
->second
.line1
));
159 void FuncEmitter::recordSourceLocation(const Location::Range
& sLoc
,
161 // Some byte codes, such as for the implicit "return 0" at the end of a
162 // a source file do not have valid source locations. This check makes
163 // sure we don't record a (dummy) source location in this case.
164 if (start
> 0 && sLoc
.line0
== -1) return;
165 SourceLoc
newLoc(sLoc
);
166 if (!m_sourceLocTab
.empty()) {
167 if (m_sourceLocTab
.back().second
== newLoc
) {
168 // Combine into the interval already at the back of the vector.
169 assertx(start
>= m_sourceLocTab
.back().first
);
172 assertx(m_sourceLocTab
.back().first
< start
&&
173 "source location offsets must be added to UnitEmitter in "
176 // First record added should be for bytecode offset zero or very rarely one
177 // when the source starts with a label and a Nop is inserted.
178 assertx(start
== 0 || start
== 1);
180 m_sourceLocTab
.push_back(std::make_pair(start
, newLoc
));
183 ///////////////////////////////////////////////////////////////////////////////
184 // Initialization and execution.
186 void FuncEmitter::init(int l1
, int l2
, Attr attrs_
,
187 const StringData
* docComment_
) {
190 attrs
= fix_attrs(attrs_
);
191 docComment
= docComment_
;
193 if (!SystemLib::s_inited
) assertx(attrs
& AttrBuiltin
);
196 void FuncEmitter::finish() {
201 s_construct("__construct"),
202 s_DynamicallyCallable("__DynamicallyCallable"),
203 s_Memoize("__Memoize"),
204 s_MemoizeLSB("__MemoizeLSB"),
205 s_KeyedByIC("KeyedByIC"),
206 s_MakeICInaccessible("MakeICInaccessible"),
207 s_SoftMakeICInaccessible("SoftMakeICInaccessible");
209 Func
* FuncEmitter::create(Unit
& unit
, PreClass
* preClass
/* = NULL */) const {
210 bool isGenerated
= isdigit(name
->data()[0]);
212 auto attrs
= fix_attrs(this->attrs
);
213 if (preClass
&& preClass
->attrs() & AttrInterface
) {
214 attrs
|= AttrAbstract
;
216 if (attrs
& AttrIsMethCaller
&& RuntimeOption::RepoAuthoritative
) {
217 attrs
|= AttrPersistent
| AttrUnique
;
219 if (attrs
& (AttrPersistent
| AttrUnique
) && !preClass
) {
220 if ((RuntimeOption::EvalJitEnableRenameFunction
||
221 attrs
& AttrInterceptable
||
222 (!RuntimeOption::RepoAuthoritative
&& SystemLib::s_inited
))) {
223 if (attrs
& AttrBuiltin
) {
224 SystemLib::s_anyNonPersistentBuiltins
= true;
226 attrs
= Attr(attrs
& ~(AttrPersistent
| AttrUnique
));
229 assertx(preClass
|| !(attrs
& AttrBuiltin
) || (attrs
& AttrIsMethCaller
));
232 attrs
|= AttrVariadicParam
;
234 if (isAsync
&& !isGenerator
) {
235 // Async functions can return results directly.
236 attrs
|= AttrSupportsAsyncEagerReturn
;
238 if (!coeffectRules
.empty()) attrs
|= AttrHasCoeffectRules
;
240 auto const dynCallSampleRate
= [&] () -> Optional
<int64_t> {
241 if (!(attrs
& AttrDynamicallyCallable
)) return {};
243 auto const uattr
= userAttributes
.find(s_DynamicallyCallable
.get());
244 if (uattr
== userAttributes
.end()) return {};
246 auto const tv
= uattr
->second
;
247 assertx(isArrayLikeType(type(tv
)));
248 auto const rate
= val(tv
).parr
->get(int64_t(0));
249 if (!isIntType(type(rate
)) || val(rate
).num
< 0) return {};
251 attrs
= Attr(attrs
& ~AttrDynamicallyCallable
);
252 return val(rate
).num
;
255 assertx(!m_pce
== !preClass
);
256 auto f
= m_ue
.newFunc(this, unit
, name
, attrs
, params
.size());
258 f
->m_isPreFunc
= !!preClass
;
260 auto const uait
= userAttributes
.find(s___Reified
.get());
261 auto const hasReifiedGenerics
= uait
!= userAttributes
.end();
263 // Returns (static coeffects, escapes)
264 auto const coeffectsInfo
= getCoeffectsInfoFromList(
266 preClass
&& name
== s_construct
.get());
267 f
->m_requiredCoeffects
= coeffectsInfo
.first
.toRequired();
269 bool const needsExtendedSharedData
=
271 line2
- line1
>= Func::kSmallDeltaLimit
||
272 m_bclen
>= Func::kSmallDeltaLimit
||
273 m_sn
>= Func::kSmallDeltaLimit
||
274 hasReifiedGenerics
||
275 hasParamsWithMultiUBs
||
276 hasReturnWithMultiUBs
||
278 coeffectsInfo
.second
.value() != 0 ||
279 !coeffectRules
.empty() ||
280 (docComment
&& !docComment
->empty());
283 needsExtendedSharedData
284 ? new Func::ExtendedSharedData(m_bc
, m_bclen
, preClass
, m_sn
, line1
, line2
,
286 : new Func::SharedData(m_bc
, m_bclen
, preClass
, m_sn
, line1
, line2
,
290 f
->init(params
.size());
292 if (auto const ex
= f
->extShared()) {
293 ex
->m_allFlags
.m_hasExtendedSharedData
= true;
294 ex
->m_arFuncPtr
= nullptr;
295 ex
->m_nativeFuncPtr
= nullptr;
296 ex
->m_bclen
= m_bclen
;
299 ex
->m_dynCallSampleRate
= dynCallSampleRate
.value_or(-1);
300 ex
->m_allFlags
.m_returnByValue
= false;
301 ex
->m_allFlags
.m_isMemoizeWrapper
= false;
302 ex
->m_allFlags
.m_isMemoizeWrapperLSB
= false;
303 ex
->m_allFlags
.m_memoizeICType
= Func::MemoizeICType::NoIC
;
305 if (!coeffectRules
.empty()) ex
->m_coeffectRules
= coeffectRules
;
306 ex
->m_coeffectEscapes
= coeffectsInfo
.second
;
307 ex
->m_docComment
= docComment
;
310 std::vector
<Func::ParamInfo
> fParams
;
311 for (unsigned i
= 0; i
< params
.size(); ++i
) {
312 Func::ParamInfo pi
= params
[i
];
313 if (pi
.isVariadic()) {
314 pi
.builtinType
= KindOfVec
;
316 fParams
.push_back(pi
);
317 auto const& fromUBs
= params
[i
].upperBounds
;
318 if (!fromUBs
.empty()) {
319 auto& ub
= f
->extShared()->m_paramUBs
[i
];
320 ub
.resize(fromUBs
.size());
321 std::copy(fromUBs
.begin(), fromUBs
.end(), ub
.begin());
322 f
->shared()->m_allFlags
.m_hasParamsWithMultiUBs
= true;
326 auto const originalFullName
=
327 (!originalFilename
||
328 !RuntimeOption::RepoAuthoritative
||
329 FileUtil::isAbsolutePath(originalFilename
->slice())) ?
331 makeStaticString(RuntimeOption::SourceRoot
+
332 originalFilename
->toCppString());
334 f
->shared()->m_localNames
.create(m_localNames
);
335 f
->shared()->m_numLocals
= m_numLocals
;
336 f
->shared()->m_numIterators
= m_numIterators
;
337 f
->m_maxStackCells
= maxStackCells
;
338 f
->shared()->m_ehtab
= ehtab
;
339 f
->shared()->m_allFlags
.m_isClosureBody
= isClosureBody
;
340 f
->shared()->m_allFlags
.m_isAsync
= isAsync
;
341 f
->shared()->m_allFlags
.m_isGenerator
= isGenerator
;
342 f
->shared()->m_allFlags
.m_isPairGenerator
= isPairGenerator
;
343 f
->shared()->m_userAttributes
= userAttributes
;
344 f
->shared()->m_retTypeConstraint
= retTypeConstraint
;
345 f
->shared()->m_retUserType
= retUserType
;
346 if (!retUpperBounds
.empty()) {
347 f
->extShared()->m_returnUBs
.resize(retUpperBounds
.size());
348 std::copy(retUpperBounds
.begin(), retUpperBounds
.end(),
349 f
->extShared()->m_returnUBs
.begin());
350 f
->shared()->m_allFlags
.m_hasReturnWithMultiUBs
= true;
352 f
->shared()->m_originalFilename
= originalFullName
;
353 f
->shared()->m_allFlags
.m_isGenerated
= isGenerated
;
354 f
->shared()->m_repoReturnType
= repoReturnType
;
355 f
->shared()->m_repoAwaitedReturnType
= repoAwaitedReturnType
;
356 f
->shared()->m_allFlags
.m_isMemoizeWrapper
= isMemoizeWrapper
;
357 f
->shared()->m_allFlags
.m_isMemoizeWrapperLSB
= isMemoizeWrapperLSB
;
358 f
->shared()->m_allFlags
.m_hasReifiedGenerics
= hasReifiedGenerics
;
360 if (isMemoizeWrapper
|| isMemoizeWrapperLSB
) {
361 auto const getICType
= [&] (TypedValue tv
) {
362 assertx(tvIsVec(tv
));
363 auto type
= Func::MemoizeICType::NoIC
;
364 IterateV(tv
.m_data
.parr
, [&](TypedValue elem
) {
365 if (tvIsString(elem
)) {
366 if (elem
.m_data
.pstr
->same(s_KeyedByIC
.get())) {
367 type
= Func::MemoizeICType::KeyedByIC
;
368 } else if (elem
.m_data
.pstr
->same(s_MakeICInaccessible
.get())) {
369 type
= Func::MemoizeICType::MakeICInaccessible
;
370 } else if (elem
.m_data
.pstr
->same(s_SoftMakeICInaccessible
.get())) {
371 type
= Func::MemoizeICType::SoftMakeICInaccessible
;
377 auto const attrName
= isMemoizeWrapperLSB
? s_MemoizeLSB
: s_Memoize
;
378 auto const it
= userAttributes
.find(attrName
.get());
379 if (it
!= userAttributes
.end()) {
380 auto const ic_type
= getICType(it
->second
);
381 assertx((ic_type
& 0x3) == ic_type
);
382 f
->shared()->m_allFlags
.m_memoizeICType
= ic_type
;
386 for (auto const& name
: staticCoeffects
) {
387 f
->shared()->m_staticCoeffectNames
.push_back(name
);
390 if (hasReifiedGenerics
) {
391 auto const tv
= uait
->second
;
392 assertx(tvIsVec(tv
));
393 f
->extShared()->m_reifiedGenericsInfo
=
394 extractSizeAndPosFromReifiedAttribute(tv
.m_data
.parr
);
398 * If we have a m_sourceLocTab, use that to create the line
399 * table. Otherwise use the line table we loaded out of the repo, or
400 * a token to lazy load it.
402 if (m_sourceLocTab
.size() != 0) {
403 f
->setLineTable(createLineTable(m_sourceLocTab
, m_bclen
));
404 // If the debugger is enabled, or we plan to dump hhas we will
405 // need the extended line table information in the output, and if
406 // we're not writing the repo, stashing it here is necessary for
407 // it to make it through.
408 if (needs_extended_line_table()) {
409 f
->stashExtendedLineTable(createSourceLocTable());
411 } else if (m_lineTable
.isPtr()) {
412 f
->setLineTable(*m_lineTable
.ptr());
414 assertx(RO::RepoAuthoritative
);
415 f
->setLineTable(m_lineTable
.token());
419 auto const ex
= f
->extShared();
421 auto const info
= getNativeInfo();
423 Attr dummy
= AttrNone
;
424 auto nativeAttributes
= parseNativeAttributes(dummy
);
425 Native::getFunctionPointers(
432 if (ex
->m_nativeFuncPtr
) {
433 if (info
.sig
.ret
== Native::NativeSig::Type::MixedTV
) {
434 ex
->m_allFlags
.m_returnByValue
= true;
436 int extra
= isMethod() ? 1 : 0;
437 assertx(info
.sig
.args
.size() == params
.size() + extra
);
438 for (auto i
= params
.size(); i
--; ) {
439 switch (info
.sig
.args
[extra
+ i
]) {
440 case Native::NativeSig::Type::ObjectArg
:
441 case Native::NativeSig::Type::StringArg
:
442 case Native::NativeSig::Type::ArrayArg
:
443 case Native::NativeSig::Type::ResourceArg
:
444 fParams
[i
].setFlag(Func::ParamInfo::Flags::NativeArg
);
446 case Native::NativeSig::Type::MixedTV
:
447 fParams
[i
].setFlag(Func::ParamInfo::Flags::NativeArg
);
448 fParams
[i
].setFlag(Func::ParamInfo::Flags::AsTypedValue
);
450 case Native::NativeSig::Type::Mixed
:
451 fParams
[i
].setFlag(Func::ParamInfo::Flags::AsVariant
);
460 f
->finishedEmittingParams(fParams
);
462 if (RuntimeOption::EvalEnableReverseDataMap
&& !preClass
) {
463 f
->registerInDataMap();
468 String
FuncEmitter::nativeFullname() const {
469 return Native::fullName(name
, m_pce
? m_pce
->name() : nullptr,
470 (attrs
& AttrStatic
));
473 Native::NativeFunctionInfo
FuncEmitter::getNativeInfo() const {
474 return Native::getNativeFunction(
477 m_pce
? m_pce
->name() : nullptr,
482 ///////////////////////////////////////////////////////////////////////////////
483 // Locals, iterators, and parameters.
485 void FuncEmitter::allocVarId(const StringData
* name
, bool slotless
) {
486 assertx(name
!= nullptr);
488 if (m_localNames
.find(name
) == m_localNames
.end()) {
489 // Slotless locals must come after all locals with slots.
491 id
= (m_numLocals
++);
492 assertx(id
== (int)m_localNames
.size());
494 m_localNames
.add(name
, name
);
498 Id
FuncEmitter::allocIterator() {
499 assertx(m_numIterators
>= m_nextFreeIterator
);
500 Id id
= m_nextFreeIterator
++;
501 if (m_numIterators
< m_nextFreeIterator
) {
502 m_numIterators
= m_nextFreeIterator
;
507 Id
FuncEmitter::allocUnnamedLocal() {
508 m_localNames
.addUnnamed(nullptr);
509 ++m_numUnnamedLocals
;
510 return m_numLocals
++;
513 ///////////////////////////////////////////////////////////////////////////////
516 EHEnt
& FuncEmitter::addEHEnt() {
517 assertx(!m_ehTabSorted
518 || "should only mark the ehtab as sorted after adding all of them");
519 ehtab
.emplace_back();
520 ehtab
.back().m_parentIndex
= 7777;
527 * Ordering on EHEnts where e1 < e2 iff
529 * a) e1 and e2 do not overlap, and e1 comes first
533 bool operator()(const EHEnt
& e1
, const EHEnt
& e2
) const {
534 if (e1
.m_base
== e2
.m_base
) {
535 return e1
.m_past
> e2
.m_past
;
537 return e1
.m_base
< e2
.m_base
;
543 void FuncEmitter::sortEHTab() {
544 if (m_ehTabSorted
) return;
546 std::sort(ehtab
.begin(), ehtab
.end(), EHEntComp());
548 for (unsigned int i
= 0; i
< ehtab
.size(); i
++) {
549 ehtab
[i
].m_parentIndex
= -1;
550 for (int j
= i
- 1; j
>= 0; j
--) {
551 if (ehtab
[j
].m_past
>= ehtab
[i
].m_past
) {
552 // parent EHEnt better enclose this one.
553 assertx(ehtab
[j
].m_base
<= ehtab
[i
].m_base
);
554 ehtab
[i
].m_parentIndex
= j
;
563 void FuncEmitter::setEHTabIsSorted() {
564 m_ehTabSorted
= true;
568 for (size_t i
= 0; i
< ehtab
.size(); ++i
) {
571 // Base offsets must be monotonically increasing.
572 always_assert(curBase
<= eh
.m_base
);
575 // Parent should come before, and must enclose this guy.
576 always_assert(eh
.m_parentIndex
== -1 || eh
.m_parentIndex
< i
);
577 if (eh
.m_parentIndex
!= -1) {
578 auto& parent
= ehtab
[eh
.m_parentIndex
];
579 always_assert(parent
.m_base
<= eh
.m_base
&&
580 parent
.m_past
>= eh
.m_past
);
585 ///////////////////////////////////////////////////////////////////////////////
588 void FuncEmitter::setBc(const unsigned char* bc
, size_t bclen
) {
590 if (auto const p
= m_bc
.ptr()) free(p
);
592 auto const p
= (unsigned char*)malloc(bclen
);
594 if (bclen
) memcpy(p
, bc
, bclen
);
595 m_bc
= Func::BCPtr::FromPtr(p
);
599 void FuncEmitter::setBcToken(Func::BCPtr::Token token
, size_t bclen
) {
601 if (auto const p
= m_bc
.ptr()) free(p
);
605 m_bc
= Func::BCPtr::FromToken(token
);
608 Optional
<Func::BCPtr::Token
> FuncEmitter::loadBc() {
609 if (m_bc
.isPtr()) return std::nullopt
;
610 assertx(RO::RepoAuthoritative
);
611 auto const old
= m_bc
.token();
612 auto bc
= (unsigned char*)malloc(m_bclen
);
613 RepoFile::readRawFromUnit(m_ue
.m_sn
, old
, bc
, m_bclen
);
614 m_bc
= Func::BCPtr::FromPtr(bc
);
618 ///////////////////////////////////////////////////////////////////////////////
621 /* <<__Native>> user attribute causes systemlib declarations
622 * to hook internal (C++) implementation of funcs/methods
624 * The Native attribute may have the following sub-options
625 * "NoFCallBuiltin": Prevent FCallBuiltin optimization
626 * Effectively forces functions to generate an ActRec
627 * "NoInjection": Do not include this frame in backtraces
629 * e.g. <<__Native("NoFCallBuiltin")>> function foo():mixed;
631 static const StaticString
632 s_native("__Native"),
633 s_nofcallbuiltin("NoFCallBuiltin"),
634 s_noinjection("NoInjection"),
635 s_opcodeimpl("OpCodeImpl");
637 int FuncEmitter::parseNativeAttributes(Attr
& attrs_
) const {
638 int ret
= Native::AttrNone
;
640 auto it
= userAttributes
.find(s_native
.get());
641 assertx(it
!= userAttributes
.end());
642 const TypedValue userAttr
= it
->second
;
643 assertx(isArrayLikeType(userAttr
.m_type
));
644 for (ArrayIter
it(userAttr
.m_data
.parr
); it
; ++it
) {
645 Variant userAttrVal
= it
.second();
646 if (userAttrVal
.isString()) {
647 String userAttrStrVal
= userAttrVal
.toString();
648 if (userAttrStrVal
.get()->isame(s_nofcallbuiltin
.get())) {
649 attrs_
|= AttrNoFCallBuiltin
;
650 } else if (userAttrStrVal
.get()->isame(s_noinjection
.get())) {
651 attrs_
|= AttrNoInjection
;
652 } else if (userAttrStrVal
.get()->isame(s_opcodeimpl
.get())) {
653 ret
|= Native::AttrOpCodeImpl
;
660 Attr
FuncEmitter::fix_attrs(Attr a
) const {
661 if (RuntimeOption::RepoAuthoritative
) return a
;
663 a
= Attr(a
& ~AttrInterceptable
);
665 if (RuntimeOption::EvalJitEnableRenameFunction
) {
666 return a
| AttrInterceptable
;
671 ///////////////////////////////////////////////////////////////////////////////
672 // Serialization/Deserialization
674 template<class SerDe
>
675 void FuncEmitter::serdeMetaData(SerDe
& sd
) {
676 // NOTE: name and a few other fields currently handled outside of this.
679 if (!SerDe::deserializing
) {
680 a
= fix_attrs(attrs
);
689 (repoAwaitedReturnType
)
697 (m_localNames
, [](auto s
) { return s
; })
699 [&](const EHEnt
& prev
, EHEnt cur
) -> EHEnt
{
700 if (!SerDe::deserializing
) {
701 cur
.m_handler
-= cur
.m_past
;
702 cur
.m_past
-= cur
.m_base
;
703 cur
.m_base
-= prev
.m_base
;
705 cur
.m_base
+= prev
.m_base
;
706 cur
.m_past
+= cur
.m_base
;
707 cur
.m_handler
+= cur
.m_past
;
720 if (SerDe::deserializing
) attrs
= fix_attrs(a
);
723 template void FuncEmitter::serdeMetaData
<>(BlobDecoder
&);
724 template void FuncEmitter::serdeMetaData
<>(BlobEncoder
&);
726 template<class SerDe
>
727 void FuncEmitter::serde(SerDe
& sd
, bool lazy
) {
728 assertx(IMPLIES(lazy
, RO::RepoAuthoritative
));
729 assertx(IMPLIES(!SerDe::deserializing
, !lazy
));
733 // Never lazy load any builtins (this avoids issues with HHBBC
734 // trying to load data after we've shutdown the repo).
735 if (attrs
& AttrBuiltin
) lazy
= false;
737 if constexpr (SerDe::deserializing
) {
739 m_lineTable
= Func::LineTablePtr::FromToken(sd
.advanced());
743 deserializeLineTable(sd
, lineTable
);
744 setLineTable(std::move(lineTable
));
747 auto const& lines
= m_sourceLocTab
.empty()
749 : createLineTable(m_sourceLocTab
, m_bclen
);
754 [&] (const LineEntry
& prev
, const LineEntry
& curDelta
) {
756 curDelta
.pastOffset() - prev
.pastOffset(),
757 curDelta
.val() - prev
.val()
765 if constexpr (SerDe::deserializing
) {
768 sd(RO::RepoDebugInfo
? m_sourceLocTab
: decltype(m_sourceLocTab
){});
772 if constexpr (SerDe::deserializing
) {
773 assertx(sd
.remaining() >= m_bclen
);
775 setBcToken(sd
.advanced(), m_bclen
);
777 setBc(sd
.data(), m_bclen
);
781 sd
.writeRaw((const char*)m_bc
.ptr(), m_bclen
);
785 template void FuncEmitter::serde
<>(BlobDecoder
&, bool);
786 template void FuncEmitter::serde
<>(BlobEncoder
&, bool);
788 void FuncEmitter::deserializeLineTable(BlobDecoder
& decoder
,
789 LineTable
& lineTable
) {
794 [&] (const LineEntry
& prev
, const LineEntry
& curDelta
) {
796 curDelta
.pastOffset() + prev
.pastOffset(),
797 curDelta
.val() + prev
.val()
805 LineTable
FuncEmitter::loadLineTableFromRepo(int64_t unitSn
,
806 RepoFile::Token token
) {
807 assertx(RO::RepoAuthoritative
);
809 auto const remaining
= RepoFile::remainingSizeOfUnit(unitSn
, token
);
810 always_assert(remaining
>= sizeof(uint64_t));
814 // We encoded the size of the line table along with the table. So,
815 // peek its size and bail if the decoder doesn't have enough data
817 auto const size
= std::min
<size_t>(remaining
, 128);
818 auto const data
= std::make_unique
<unsigned char[]>(size
);
819 RepoFile::readRawFromUnit(unitSn
, token
, data
.get(), size
);
820 BlobDecoder decoder
{data
.get(), size
};
821 actualSize
= decoder
.peekSize();
822 if (actualSize
<= decoder
.remaining()) {
824 deserializeLineTable(decoder
, lineTable
);
829 constexpr const size_t kLineTableSizeLimit
= 1ull << 32;
830 always_assert(actualSize
<= kLineTableSizeLimit
);
831 always_assert(actualSize
<= remaining
);
834 auto const data
= std::make_unique
<unsigned char[]>(actualSize
);
835 RepoFile::readRawFromUnit(unitSn
, token
, data
.get(), actualSize
);
836 BlobDecoder decoder
{data
.get(), actualSize
};
837 deserializeLineTable(decoder
, lineTable
);
838 decoder
.assertDone();
842 ///////////////////////////////////////////////////////////////////////////////