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/record-replay.h"
23 #include "hphp/runtime/base/runtime-option.h"
25 #include "hphp/runtime/ext/extension.h"
27 #include "hphp/runtime/vm/bytecode.h"
28 #include "hphp/runtime/vm/native.h"
29 #include "hphp/runtime/vm/preclass-emitter.h"
30 #include "hphp/runtime/vm/reified-generics.h"
31 #include "hphp/runtime/vm/repo-autoload-map-builder.h"
32 #include "hphp/runtime/vm/repo-file.h"
33 #include "hphp/runtime/vm/runtime.h"
35 #include "hphp/runtime/vm/jit/types.h"
37 #include "hphp/runtime/vm/verifier/cfg.h"
39 #include "hphp/system/systemlib.h"
41 #include "hphp/util/atomic-vector.h"
42 #include "hphp/util/blob-encoder.h"
43 #include "hphp/util/file.h"
44 #include "hphp/util/trace.h"
49 ///////////////////////////////////////////////////////////////////////////////
52 FuncEmitter::FuncEmitter(UnitEmitter
& ue
, int sn
, Id id
, const StringData
* n
)
62 , retUserType(nullptr)
64 , originalFilename(nullptr)
65 , originalModuleName(nullptr)
66 , memoizePropName(nullptr)
67 , memoizeGuardPropName(nullptr)
68 , memoizeSharedPropIndex(0)
70 , m_numUnnamedLocals(0)
72 , m_nextFreeIterator(0)
73 , m_ehTabSorted(false)
76 FuncEmitter::FuncEmitter(UnitEmitter
& ue
, int sn
, const StringData
* n
,
87 , retUserType(nullptr)
89 , originalFilename(nullptr)
90 , originalModuleName(nullptr)
91 , memoizePropName(nullptr)
92 , memoizeGuardPropName(nullptr)
93 , memoizeSharedPropIndex(0)
95 , m_numUnnamedLocals(0)
97 , m_nextFreeIterator(0)
98 , m_ehTabSorted(false)
101 FuncEmitter::~FuncEmitter() {
103 if (auto const p
= m_bc
.ptr()) free(p
);
105 if (m_lineTable
.isPtr()) {
106 if (auto const p
= m_lineTable
.ptr()) delete p
;
111 ///////////////////////////////////////////////////////////////////////////////
114 SourceLocTable
FuncEmitter::createSourceLocTable() const {
115 assertx(m_sourceLocTab
.size() != 0);
116 SourceLocTable locations
;
117 for (size_t i
= 0; i
< m_sourceLocTab
.size(); ++i
) {
118 Offset endOff
= i
< m_sourceLocTab
.size() - 1
119 ? m_sourceLocTab
[i
+ 1].first
121 locations
.push_back(SourceLocEntry(endOff
, m_sourceLocTab
[i
].second
));
126 void FuncEmitter::setLineTable(LineTable table
) {
127 if (m_lineTable
.isPtr()) {
128 if (auto p
= m_lineTable
.ptr()) {
129 *p
= std::move(table
);
133 m_lineTable
= Func::LineTablePtr::FromPtr(new LineTable
{std::move(table
)});
138 using SrcLoc
= std::vector
<std::pair
<Offset
, SourceLoc
>>;
141 * Create a LineTable from `srcLoc'.
143 LineTable
createLineTable(const SrcLoc
& srcLoc
, Offset bclen
) {
145 if (srcLoc
.empty()) {
149 auto prev
= srcLoc
.begin();
150 for (auto it
= prev
+ 1; it
!= srcLoc
.end(); ++it
) {
151 if (prev
->second
.line1
!= it
->second
.line1
) {
152 lines
.push_back(LineEntry(it
->first
, prev
->second
.line1
));
157 lines
.push_back(LineEntry(bclen
, prev
->second
.line1
));
163 void FuncEmitter::recordSourceLocation(const Location::Range
& sLoc
,
165 // Some byte codes, such as for the implicit "return 0" at the end of a
166 // a source file do not have valid source locations. This check makes
167 // sure we don't record a (dummy) source location in this case.
168 if (start
> 0 && sLoc
.line0
== -1) return;
169 SourceLoc
newLoc(sLoc
);
170 if (!m_sourceLocTab
.empty()) {
171 if (m_sourceLocTab
.back().second
== newLoc
) {
172 // Combine into the interval already at the back of the vector.
173 assertx(start
>= m_sourceLocTab
.back().first
);
176 assertx(m_sourceLocTab
.back().first
< start
&&
177 "source location offsets must be added to UnitEmitter in "
180 // First record added should be for bytecode offset zero or very rarely one
181 // when the source starts with a label and a Nop is inserted.
182 assertx(start
== 0 || start
== 1);
184 m_sourceLocTab
.push_back(std::make_pair(start
, newLoc
));
187 ///////////////////////////////////////////////////////////////////////////////
188 // Initialization and execution.
190 void FuncEmitter::init(int l1
, int l2
, Attr attrs_
,
191 const StringData
* docComment_
) {
194 docComment
= docComment_
;
197 assertx(!ue().isASystemLib() || attrs
& AttrBuiltin
);
200 void FuncEmitter::finish() {
205 s_construct("__construct"),
206 s_DynamicallyCallable("__DynamicallyCallable"),
207 s_Memoize("__Memoize"),
208 s_MemoizeLSB("__MemoizeLSB"),
209 s_KeyedByIC("KeyedByIC"),
210 s_MakeICInaccessible("MakeICInaccessible"),
211 s_SoftMakeICInaccessible("SoftMakeICInaccessible"),
212 s_Uncategorized("Uncategorized"),
213 s_SoftInternal("__SoftInternal");
216 bool is_interceptable(const PreClass
* preClass
, const StringData
* name
) {
217 if (RO::RepoAuthoritative
) return false;
218 if (Cfg::Eval::NonInterceptableFunctions
.empty()) return true;
219 auto fullname
= [&]() {
220 auto n
= name
->toCppString();
224 return preClass
->name()->toCppString() +"::"+ n
;
226 return Cfg::Eval::NonInterceptableFunctions
.count(fullname
) == 0;
230 Func
* FuncEmitter::create(Unit
& unit
, PreClass
* preClass
/* = NULL */) const {
231 auto attrs
= this->attrs
;
233 auto persistent
= RO::RepoAuthoritative
|| (ue().isASystemLib() && (!RO::funcIsRenamable(name
) || preClass
));
234 assertx(IMPLIES(attrs
& AttrPersistent
, persistent
));
235 attrSetter(attrs
, persistent
, AttrPersistent
);
237 auto interceptable
= is_interceptable(preClass
, name
);
238 attrSetter(attrs
, interceptable
, AttrInterceptable
);
240 if (!(attrs
& AttrPersistent
) && attrs
& AttrBuiltin
) {
242 SystemLib::s_anyNonPersistentBuiltins
= true;
245 if (isAsync
&& !isGenerator
) {
246 // Async functions can return results directly.
247 attrs
|= AttrSupportsAsyncEagerReturn
;
249 if (!coeffectRules
.empty()) attrs
|= AttrHasCoeffectRules
;
251 if (attrs
& AttrInternal
&&
252 userAttributes
.find(s_SoftInternal
.get()) != userAttributes
.end()) {
253 attrs
|= AttrInternalSoft
;
256 if (hasVar(s_86productAttributionData
.get())) {
257 attrs
|= AttrHasAttributionData
;
260 auto const dynCallSampleRate
= [&] () -> Optional
<int64_t> {
261 if (!(attrs
& AttrDynamicallyCallable
)) return {};
263 auto const uattr
= userAttributes
.find(s_DynamicallyCallable
.get());
264 if (uattr
== userAttributes
.end()) return {};
266 auto const tv
= uattr
->second
;
267 assertx(isArrayLikeType(type(tv
)));
268 auto const rate
= val(tv
).parr
->get(int64_t(0));
269 if (!isIntType(type(rate
)) || val(rate
).num
< 0) return {};
271 attrs
= Attr(attrs
& ~AttrDynamicallyCallable
);
272 return val(rate
).num
;
275 assertx(!m_pce
== !preClass
);
276 auto f
= m_ue
.newFunc(this, unit
, name
, attrs
, params
.size());
278 f
->m_isPreFunc
= !!preClass
;
280 auto const uait
= userAttributes
.find(s___Reified
.get());
281 auto const hasReifiedGenerics
= uait
!= userAttributes
.end();
283 // Returns (static coeffects, escapes)
284 auto const coeffectsInfo
= getCoeffectsInfoFromList(
286 preClass
&& name
== s_construct
.get());
287 f
->m_requiredCoeffects
= coeffectsInfo
.first
.toRequired();
289 Func::MemoizeICType icType
= Func::MemoizeICType::MakeICInaccessible
;
290 if (isMemoizeWrapper
|| isMemoizeWrapperLSB
) {
291 auto const getICType
= [&] (TypedValue tv
) {
292 assertx(tvIsVec(tv
));
293 IterateV(tv
.m_data
.parr
, [&](TypedValue elem
) {
294 if (tvIsString(elem
)) {
296 * TODO: simplify this into a 2 state if else once Soft states are
299 if (elem
.m_data
.pstr
->same(s_KeyedByIC
.get())) {
300 assertx(tv
.m_data
.parr
->size() == 1);
301 icType
= Func::MemoizeICType::KeyedByIC
;
302 } else if (elem
.m_data
.pstr
->same(s_MakeICInaccessible
.get())) {
303 assertx(tv
.m_data
.parr
->size() == 1);
304 icType
= Func::MemoizeICType::MakeICInaccessible
;
305 } else if (elem
.m_data
.pstr
->same(s_SoftMakeICInaccessible
.get())) {
306 assertx(tv
.m_data
.parr
->size() <= 2);
307 icType
= Func::MemoizeICType::MakeICInaccessible
;
308 } else if (elem
.m_data
.pstr
->same(s_Uncategorized
.get())) {
309 icType
= Func::MemoizeICType::MakeICInaccessible
;
311 assertx(false && "invalid string");
313 } else if (tvIsInt(elem
)) {
315 * This would be the sample rate, we don't use it
316 * this branch will be removed once the SoftMakeICInaccessible
317 * is removed from www
318 * for now assert that the SoftMakeICInaccessible was mapped to non-soft
320 assertx(icType
== Func::MemoizeICType::MakeICInaccessible
);
322 assertx(false && "invalid input");
326 auto const attrName
= isMemoizeWrapperLSB
? s_MemoizeLSB
: s_Memoize
;
327 auto const it
= userAttributes
.find(attrName
.get());
328 if (it
!= userAttributes
.end()) {
329 getICType(it
->second
);
330 assertx((icType
& 0x3) == icType
);
334 bool const needsExtendedSharedData
=
336 line2
- line1
>= Func::kSmallDeltaLimit
||
337 m_bclen
>= Func::kSmallDeltaLimit
||
338 m_sn
>= Func::kSmallDeltaLimit
||
339 hasReifiedGenerics
||
340 hasParamsWithMultiUBs
||
341 hasReturnWithMultiUBs
||
343 coeffectsInfo
.second
.value() != 0 ||
344 !coeffectRules
.empty() ||
345 (docComment
&& !docComment
->empty()) ||
346 requiresFromOriginalModule
;
349 needsExtendedSharedData
350 ? new Func::ExtendedSharedData(m_bc
, m_bclen
, preClass
, m_sn
, line1
, line2
,
352 : new Func::SharedData(m_bc
, m_bclen
, preClass
, m_sn
, line1
, line2
,
356 f
->init(params
.size());
358 if (auto const ex
= f
->extShared()) {
359 ex
->m_allFlags
.m_hasExtendedSharedData
= true;
360 ex
->m_arFuncPtr
= nullptr;
361 ex
->m_nativeFuncPtr
= nullptr;
362 ex
->m_bclen
= m_bclen
;
365 ex
->m_dynCallSampleRate
= dynCallSampleRate
.value_or(-1);
366 ex
->m_allFlags
.m_returnByValue
= false;
367 ex
->m_allFlags
.m_isUntrustedReturnType
= false;
368 ex
->m_allFlags
.m_isMemoizeWrapper
= false;
369 ex
->m_allFlags
.m_isMemoizeWrapperLSB
= false;
370 ex
->m_allFlags
.m_memoizeICType
= Func::MemoizeICType::NoIC
;
372 if (!coeffectRules
.empty()) ex
->m_coeffectRules
= coeffectRules
;
373 ex
->m_coeffectEscapes
= coeffectsInfo
.second
;
374 ex
->m_docComment
= docComment
;
375 ex
->m_originalModuleName
=
376 originalModuleName
? originalModuleName
: LowStringPtr(unit
.moduleName());
377 assertx(ex
->m_originalModuleName
);
380 std::vector
<Func::ParamInfo
> fParams
;
381 for (unsigned i
= 0; i
< params
.size(); ++i
) {
382 Func::ParamInfo pi
= params
[i
];
383 fParams
.push_back(pi
);
384 auto const& fromUBs
= params
[i
].upperBounds
;
385 if (!fromUBs
.isTop()) {
386 auto& ub
= f
->extShared()->m_paramUBs
[i
];
387 ub
.m_constraints
.resize(fromUBs
.m_constraints
.size());
388 std::copy(fromUBs
.m_constraints
.begin(), fromUBs
.m_constraints
.end(), ub
.m_constraints
.begin());
389 f
->shared()->m_allFlags
.m_hasParamsWithMultiUBs
= true;
393 auto const originalFullName
=
394 (!originalFilename
||
395 !RuntimeOption::RepoAuthoritative
||
396 FileUtil::isAbsolutePath(originalFilename
->slice())) ?
398 makeStaticString(Cfg::Server::SourceRoot
+
399 originalFilename
->toCppString());
401 f
->shared()->m_localNames
.create(m_localNames
);
402 f
->shared()->m_numLocals
= m_numLocals
;
403 f
->shared()->m_numIterators
= m_numIterators
;
404 f
->m_maxStackCells
= maxStackCells
;
405 f
->shared()->m_ehtab
= ehtab
;
406 f
->shared()->m_allFlags
.m_isClosureBody
= isClosureBody
;
407 f
->shared()->m_allFlags
.m_isAsync
= isAsync
;
408 f
->shared()->m_allFlags
.m_isGenerator
= isGenerator
;
409 f
->shared()->m_allFlags
.m_isPairGenerator
= isPairGenerator
;
410 f
->shared()->m_userAttributes
= userAttributes
;
411 f
->shared()->m_retTypeConstraint
= retTypeConstraint
;
412 f
->shared()->m_retUserType
= retUserType
;
413 if (!retUpperBounds
.isTop()) {
414 f
->extShared()->m_returnUBs
.m_constraints
.resize(retUpperBounds
.m_constraints
.size());
415 std::copy(retUpperBounds
.m_constraints
.begin(), retUpperBounds
.m_constraints
.end(),
416 f
->extShared()->m_returnUBs
.m_constraints
.begin());
417 f
->shared()->m_allFlags
.m_hasReturnWithMultiUBs
= true;
419 f
->shared()->m_originalFilename
= originalFullName
;
420 f
->shared()->m_allFlags
.m_isGenerated
= HPHP::is_generated(name
);
421 f
->shared()->m_repoReturnType
= repoReturnType
;
422 f
->shared()->m_repoAwaitedReturnType
= repoAwaitedReturnType
;
423 f
->shared()->m_allFlags
.m_isMemoizeWrapper
= isMemoizeWrapper
;
424 f
->shared()->m_allFlags
.m_isMemoizeWrapperLSB
= isMemoizeWrapperLSB
;
425 f
->shared()->m_allFlags
.m_memoizeICType
= icType
;
426 f
->shared()->m_allFlags
.m_hasReifiedGenerics
= hasReifiedGenerics
;
428 for (auto const& name
: staticCoeffects
) {
429 f
->shared()->m_staticCoeffectNames
.push_back(name
);
432 if (hasReifiedGenerics
) {
433 auto const tv
= uait
->second
;
434 assertx(tvIsVec(tv
));
435 f
->extShared()->m_reifiedGenericsInfo
=
436 extractSizeAndPosFromReifiedAttribute(tv
.m_data
.parr
);
440 * If we have a m_sourceLocTab, use that to create the line
441 * table. Otherwise use the line table we loaded out of the repo, or
442 * a token to lazy load it.
444 if (m_sourceLocTab
.size() != 0) {
445 f
->setLineTable(createLineTable(m_sourceLocTab
, m_bclen
));
446 // If the debugger is enabled, or we plan to dump hhas we will
447 // need the extended line table information in the output, and if
448 // we're not writing the repo, stashing it here is necessary for
449 // it to make it through.
450 if (needs_extended_line_table()) {
451 f
->stashExtendedLineTable(createSourceLocTable());
453 } else if (m_lineTable
.isPtr()) {
454 f
->setLineTable(*m_lineTable
.ptr());
456 assertx(RO::RepoAuthoritative
);
457 f
->setLineTable(m_lineTable
.token());
461 auto const ex
= f
->extShared();
463 auto const info
= getNativeInfo();
465 Attr dummy
= AttrNone
;
466 auto nativeAttributes
= parseNativeAttributes(dummy
);
467 Native::getFunctionPointers(
474 if (ex
->m_nativeFuncPtr
) {
475 if (UNLIKELY(RO::EvalRecordReplay
)) {
476 rr::addNativeFuncAttrs(ex
->m_nativeFuncPtr
, attrs
);
478 if (info
.sig
.ret
== Native::NativeSig::Type::MixedTV
||
479 info
.sig
.ret
== Native::NativeSig::Type::StringNN
||
480 info
.sig
.ret
== Native::NativeSig::Type::ArrayNN
||
481 info
.sig
.ret
== Native::NativeSig::Type::ObjectNN
) {
482 // the non null ref or mixed TV should be return by value
483 ex
->m_allFlags
.m_returnByValue
= true;
485 if (info
.sig
.ret
== Native::NativeSig::Type::String
||
486 info
.sig
.ret
== Native::NativeSig::Type::Array
||
487 info
.sig
.ret
== Native::NativeSig::Type::Object
||
488 info
.sig
.ret
== Native::NativeSig::Type::Resource
) {
489 // these types are nullable
490 // set return by value flag
491 ex
->m_allFlags
.m_isUntrustedReturnType
= true;
494 int extra
= isMethod() ? 1 : 0;
495 assertx(info
.sig
.args
.size() == params
.size() + extra
);
496 for (auto i
= params
.size(); i
--; ) {
497 switch (info
.sig
.args
[extra
+ i
]) {
498 case Native::NativeSig::Type::ObjectNN
:
499 case Native::NativeSig::Type::StringNN
:
500 case Native::NativeSig::Type::ArrayNN
:
501 case Native::NativeSig::Type::ResourceArg
:
502 fParams
[i
].setFlag(Func::ParamInfo::Flags::NativeArg
);
504 case Native::NativeSig::Type::MixedTV
:
505 fParams
[i
].setFlag(Func::ParamInfo::Flags::NativeArg
);
506 fParams
[i
].setFlag(Func::ParamInfo::Flags::AsTypedValue
);
508 case Native::NativeSig::Type::Mixed
:
509 fParams
[i
].setFlag(Func::ParamInfo::Flags::AsVariant
);
518 f
->finishedEmittingParams(fParams
);
520 if (RuntimeOption::EvalEnableReverseDataMap
&& !preClass
) {
521 f
->registerInDataMap();
526 String
FuncEmitter::nativeFullname() const {
527 return Native::fullName(name
, m_pce
? m_pce
->name() : nullptr,
528 (attrs
& AttrStatic
));
531 Native::NativeFunctionInfo
FuncEmitter::getNativeInfo() const {
532 assertx(m_ue
.m_extension
);
533 return Native::getNativeFunction(
534 m_ue
.m_extension
->nativeFuncs(),
536 m_pce
? m_pce
->name() : nullptr,
541 ///////////////////////////////////////////////////////////////////////////////
542 // Locals, iterators, and parameters.
544 void FuncEmitter::allocVarId(const StringData
* name
, bool slotless
) {
545 assertx(name
!= nullptr);
547 if (m_localNames
.find(name
) == m_localNames
.end()) {
548 // Slotless locals must come after all locals with slots.
550 id
= (m_numLocals
++);
551 assertx(id
== (int)m_localNames
.size());
553 m_localNames
.add(name
, name
);
557 Id
FuncEmitter::allocIterator() {
558 assertx(m_numIterators
>= m_nextFreeIterator
);
559 Id id
= m_nextFreeIterator
++;
560 if (m_numIterators
< m_nextFreeIterator
) {
561 m_numIterators
= m_nextFreeIterator
;
566 Id
FuncEmitter::allocUnnamedLocal() {
567 m_localNames
.addUnnamed(nullptr);
568 ++m_numUnnamedLocals
;
569 return m_numLocals
++;
572 ///////////////////////////////////////////////////////////////////////////////
575 EHEnt
& FuncEmitter::addEHEnt() {
576 assertx(!m_ehTabSorted
577 || "should only mark the ehtab as sorted after adding all of them");
578 ehtab
.emplace_back();
579 ehtab
.back().m_parentIndex
= 7777;
586 * Ordering on EHEnts where e1 < e2 iff
588 * a) e1 and e2 do not overlap, and e1 comes first
592 bool operator()(const EHEnt
& e1
, const EHEnt
& e2
) const {
593 if (e1
.m_base
== e2
.m_base
) {
594 return e1
.m_past
> e2
.m_past
;
596 return e1
.m_base
< e2
.m_base
;
602 void FuncEmitter::sortEHTab() {
603 if (m_ehTabSorted
) return;
605 std::sort(ehtab
.begin(), ehtab
.end(), EHEntComp());
607 for (unsigned int i
= 0; i
< ehtab
.size(); i
++) {
608 ehtab
[i
].m_parentIndex
= -1;
609 for (int j
= i
- 1; j
>= 0; j
--) {
610 if (ehtab
[j
].m_past
>= ehtab
[i
].m_past
) {
611 // parent EHEnt better enclose this one.
612 assertx(ehtab
[j
].m_base
<= ehtab
[i
].m_base
);
613 ehtab
[i
].m_parentIndex
= j
;
622 void FuncEmitter::setEHTabIsSorted() {
623 m_ehTabSorted
= true;
627 for (size_t i
= 0; i
< ehtab
.size(); ++i
) {
630 // Base offsets must be monotonically increasing.
631 always_assert(curBase
<= eh
.m_base
);
634 // Parent should come before, and must enclose this guy.
635 always_assert(eh
.m_parentIndex
== -1 || eh
.m_parentIndex
< i
);
636 if (eh
.m_parentIndex
!= -1) {
637 auto& parent
= ehtab
[eh
.m_parentIndex
];
638 always_assert(parent
.m_base
<= eh
.m_base
&&
639 parent
.m_past
>= eh
.m_past
);
644 ///////////////////////////////////////////////////////////////////////////////
647 void FuncEmitter::setBc(const unsigned char* bc
, size_t bclen
) {
649 if (auto const p
= m_bc
.ptr()) free(p
);
651 auto const p
= (unsigned char*)malloc(bclen
);
653 if (bclen
) memcpy(p
, bc
, bclen
);
654 m_bc
= Func::BCPtr::FromPtr(p
);
658 void FuncEmitter::setBcToken(Func::BCPtr::Token token
, size_t bclen
) {
660 if (auto const p
= m_bc
.ptr()) free(p
);
664 m_bc
= Func::BCPtr::FromToken(token
);
667 Optional
<Func::BCPtr::Token
> FuncEmitter::loadBc() {
668 if (m_bc
.isPtr()) return std::nullopt
;
669 assertx(RO::RepoAuthoritative
);
670 auto const old
= m_bc
.token();
671 auto bc
= (unsigned char*)malloc(m_bclen
);
672 RepoFile::readRawFromUnit(m_ue
.m_sn
, old
, bc
, m_bclen
);
673 m_bc
= Func::BCPtr::FromPtr(bc
);
677 ///////////////////////////////////////////////////////////////////////////////
680 /* <<__Native>> user attribute causes systemlib declarations
681 * to hook internal (C++) implementation of funcs/methods
683 * The Native attribute may have the following sub-options
684 * "NoFCallBuiltin": Prevent FCallBuiltin optimization
685 * Effectively forces functions to generate an ActRec
686 * "NoInjection": Do not include this frame in backtraces
687 * "NoRecording": Do not include calls to this when recording
689 * e.g. <<__Native("NoFCallBuiltin")>> function foo():mixed;
691 static const StaticString
692 s_native("__Native"),
693 s_nofcallbuiltin("NoFCallBuiltin"),
694 s_noinjection("NoInjection"),
695 s_norecording("NoRecording"),
696 s_opcodeimpl("OpCodeImpl");
698 int FuncEmitter::parseNativeAttributes(Attr
& attrs_
) const {
699 int ret
= Native::AttrNone
;
701 auto it
= userAttributes
.find(s_native
.get());
702 assertx(it
!= userAttributes
.end());
703 const TypedValue userAttr
= it
->second
;
704 assertx(isArrayLikeType(userAttr
.m_type
));
705 for (ArrayIter
it(userAttr
.m_data
.parr
); it
; ++it
) {
706 Variant userAttrVal
= it
.second();
707 if (userAttrVal
.isString()) {
708 String userAttrStrVal
= userAttrVal
.toString();
709 if (userAttrStrVal
.get()->tsame(s_nofcallbuiltin
.get())) {
710 attrs_
|= AttrNoFCallBuiltin
;
711 } else if (userAttrStrVal
.get()->tsame(s_noinjection
.get())) {
712 attrs_
|= AttrNoInjection
;
713 } else if (userAttrStrVal
.get()->tsame(s_norecording
.get())) {
714 attrs_
|= AttrNoRecording
;
715 } else if (userAttrStrVal
.get()->tsame(s_opcodeimpl
.get())) {
716 ret
|= Native::AttrOpCodeImpl
;
723 ///////////////////////////////////////////////////////////////////////////////
724 // Serialization/Deserialization
726 template<class SerDe
>
727 void FuncEmitter::serdeMetaData(SerDe
& sd
) {
728 // NOTE: name and a few other fields currently handled outside of this.
736 (repoAwaitedReturnType
)
744 (m_localNames
, [](auto s
) { return s
; })
747 [&](const EHEnt
& prev
, EHEnt cur
) -> EHEnt
{
748 if constexpr (!SerDe::deserializing
) {
749 cur
.m_handler
-= cur
.m_past
;
750 cur
.m_past
-= cur
.m_base
;
751 cur
.m_base
-= prev
.m_base
;
753 cur
.m_base
+= prev
.m_base
;
754 cur
.m_past
+= cur
.m_base
;
755 cur
.m_handler
+= cur
.m_past
;
770 template void FuncEmitter::serdeMetaData
<>(BlobDecoder
&);
771 template void FuncEmitter::serdeMetaData
<>(BlobEncoder
&);
773 template<class SerDe
>
774 void FuncEmitter::serde(SerDe
& sd
, bool lazy
) {
775 assertx(IMPLIES(lazy
, RO::RepoAuthoritative
));
776 assertx(IMPLIES(!SerDe::deserializing
, !lazy
));
780 // Never lazy load any builtins (this avoids issues with HHBBC
781 // trying to load data after we've shutdown the repo).
782 if (attrs
& AttrBuiltin
) lazy
= false;
784 if constexpr (SerDe::deserializing
) {
786 m_lineTable
= Func::LineTablePtr::FromToken(sd
.advanced());
790 deserializeLineTable(sd
, lineTable
);
791 setLineTable(std::move(lineTable
));
794 auto const& lines
= m_sourceLocTab
.empty()
796 : createLineTable(m_sourceLocTab
, m_bclen
);
801 [&] (const LineEntry
& prev
, const LineEntry
& curDelta
) {
803 curDelta
.pastOffset() - prev
.pastOffset(),
804 curDelta
.val() - prev
.val()
812 if constexpr (SerDe::deserializing
) {
815 sd(RO::RepoDebugInfo
? m_sourceLocTab
: decltype(m_sourceLocTab
){});
819 if constexpr (SerDe::deserializing
) {
820 assertx(sd
.remaining() >= m_bclen
);
822 setBcToken(sd
.advanced(), m_bclen
);
824 setBc(sd
.data(), m_bclen
);
828 sd
.writeRaw((const char*)m_bc
.ptr(), m_bclen
);
832 template void FuncEmitter::serde
<>(BlobDecoder
&, bool);
833 template void FuncEmitter::serde
<>(BlobEncoder
&, bool);
835 void FuncEmitter::deserializeLineTable(BlobDecoder
& decoder
,
836 LineTable
& lineTable
) {
841 [&] (const LineEntry
& prev
, const LineEntry
& curDelta
) {
843 curDelta
.pastOffset() + prev
.pastOffset(),
844 curDelta
.val() + prev
.val()
852 LineTable
FuncEmitter::loadLineTableFromRepo(int64_t unitSn
,
853 RepoFile::Token token
) {
854 assertx(RO::RepoAuthoritative
);
856 auto const remaining
= RepoFile::remainingSizeOfUnit(unitSn
, token
);
857 always_assert(remaining
>= sizeof(uint64_t));
861 // We encoded the size of the line table along with the table. So,
862 // peek its size and bail if the decoder doesn't have enough data
864 auto const size
= std::min
<size_t>(remaining
, 128);
865 auto const data
= std::make_unique
<unsigned char[]>(size
);
866 RepoFile::readRawFromUnit(unitSn
, token
, data
.get(), size
);
867 BlobDecoder decoder
{data
.get(), size
};
868 actualSize
= decoder
.peekSize();
869 if (actualSize
<= decoder
.remaining()) {
871 deserializeLineTable(decoder
, lineTable
);
876 constexpr const size_t kLineTableSizeLimit
= 1ull << 32;
877 always_assert(actualSize
<= kLineTableSizeLimit
);
878 always_assert(actualSize
<= remaining
);
881 auto const data
= std::make_unique
<unsigned char[]>(actualSize
);
882 RepoFile::readRawFromUnit(unitSn
, token
, data
.get(), actualSize
);
883 BlobDecoder decoder
{data
.get(), actualSize
};
884 deserializeLineTable(decoder
, lineTable
);
885 decoder
.assertDone();
889 ///////////////////////////////////////////////////////////////////////////////