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/unit.h"
29 #include <boost/container/flat_map.hpp>
30 #include <boost/algorithm/string/predicate.hpp>
32 #include <folly/Format.h>
34 #include <tbb/concurrent_hash_map.h>
36 #include "hphp/util/alloc.h"
37 #include "hphp/util/assertions.h"
38 #include "hphp/util/compilation-flags.h"
39 #include "hphp/util/functional.h"
40 #include "hphp/util/lock.h"
41 #include "hphp/util/mutex.h"
42 #include "hphp/util/smalllocks.h"
43 #include "hphp/util/struct-log.h"
45 #include "hphp/runtime/base/attr.h"
46 #include "hphp/runtime/base/array-iterator.h"
47 #include "hphp/runtime/base/autoload-handler.h"
48 #include "hphp/runtime/base/execution-context.h"
49 #include "hphp/runtime/base/file-util.h"
50 #include "hphp/runtime/base/packed-array.h"
51 #include "hphp/runtime/base/rds.h"
52 #include "hphp/runtime/base/runtime-error.h"
53 #include "hphp/runtime/base/runtime-option.h"
54 #include "hphp/runtime/base/stats.h"
55 #include "hphp/runtime/base/string-data.h"
56 #include "hphp/runtime/base/strings.h"
57 #include "hphp/runtime/base/tv-mutate.h"
58 #include "hphp/runtime/base/tv-variant.h"
59 #include "hphp/runtime/base/tv-refcount.h"
60 #include "hphp/runtime/base/type-array.h"
61 #include "hphp/runtime/base/type-string.h"
62 #include "hphp/runtime/base/type-variant.h"
63 #include "hphp/runtime/base/typed-value.h"
64 #include "hphp/runtime/base/unit-cache.h"
66 #include "hphp/runtime/vm/bytecode.h"
67 #include "hphp/runtime/vm/class.h"
68 #include "hphp/runtime/vm/debug/debug.h"
69 #include "hphp/runtime/vm/debugger-hook.h"
70 #include "hphp/runtime/vm/func.h"
71 #include "hphp/runtime/vm/hh-utils.h"
72 #include "hphp/runtime/vm/hhbc-codec.h"
73 #include "hphp/runtime/vm/hhbc.h"
74 #include "hphp/runtime/vm/instance-bits.h"
75 #include "hphp/runtime/vm/named-entity.h"
76 #include "hphp/runtime/vm/named-entity-defs.h"
77 #include "hphp/runtime/vm/preclass.h"
78 #include "hphp/runtime/vm/record.h"
79 #include "hphp/runtime/vm/repo.h"
80 #include "hphp/runtime/vm/reverse-data-map.h"
81 #include "hphp/runtime/vm/treadmill.h"
82 #include "hphp/runtime/vm/type-alias.h"
83 #include "hphp/runtime/vm/unit-emitter.h"
84 #include "hphp/runtime/vm/unit-util.h"
85 #include "hphp/runtime/vm/vm-regs.h"
87 #include "hphp/runtime/server/memory-stats.h"
88 #include "hphp/runtime/server/source-root-info.h"
90 #include "hphp/runtime/ext/std/ext_std_closure.h"
91 #include "hphp/runtime/ext/string/ext_string.h"
93 #include "hphp/system/systemlib.h"
94 #include "hphp/runtime/base/program-functions.h"
98 //////////////////////////////////////////////////////////////////////
103 //////////////////////////////////////////////////////////////////////
105 const StaticString
s_stdin("STDIN");
106 const StaticString
s_stdout("STDOUT");
107 const StaticString
s_stderr("STDERR");
109 //////////////////////////////////////////////////////////////////////
113 ///////////////////////////////////////////////////////////////////////////////
116 Unit::MergeInfo
* Unit::MergeInfo::alloc(size_t size
) {
117 MergeInfo
* mi
= (MergeInfo
*)malloc(
118 sizeof(MergeInfo
) + size
* sizeof(void*));
119 mi
->m_firstHoistablePreClass
= 0;
120 mi
->m_firstMergeablePreClass
= 0;
121 mi
->m_mergeablesSize
= size
;
126 ///////////////////////////////////////////////////////////////////////////////
127 // Construction and destruction.
130 : m_interpretOnly(false)
132 , m_serialized(false)
137 if (RuntimeOption::EvalEnableReverseDataMap
&&
138 m_mergeState
.load(std::memory_order_relaxed
) != MergeState::Unmerged
) {
139 // Units are registered to data_map in Unit::initialMerge().
140 data_map::deregister(this);
143 SourceLocation::removeUnit(this);
145 if (!RuntimeOption::RepoAuthoritative
) {
147 // poison released bytecode
148 memset(const_cast<unsigned char*>(m_bc
), 0xff, m_bclen
);
150 free(const_cast<unsigned char*>(m_bc
));
151 g_hhbc_size
->addValue(-int64_t(m_bclen
));
154 auto const mi
= mergeInfo();
156 for (auto const func
: mi
->mutableFuncs()) Func::destroy(func
);
159 // ExecutionContext and the TC may retain references to Class'es, so
160 // it is possible for Class'es to outlive their Unit.
161 for (auto const& pcls
: m_preClasses
) {
162 Class
* cls
= pcls
->namedEntity()->clsList();
166 if (cur
->preClass() == pcls
.get()) {
172 for (auto const& rec
: m_preRecords
) {
173 RecordDesc
* recList
= rec
->namedEntity()->recordList();
175 RecordDesc
* cur
= recList
;
176 recList
= recList
->m_next
;
177 if (cur
->preRecordDesc() == rec
.get()) {
186 void* Unit::operator new(size_t sz
) {
187 MemoryStats::LogAlloc(AllocKind::Unit
, sz
);
188 return low_malloc(sz
);
191 void Unit::operator delete(void* p
, size_t /*sz*/) {
196 ///////////////////////////////////////////////////////////////////////////////
199 int Unit::getLineNumber(Offset pc
) const {
200 if (UNLIKELY(m_repoId
== RepoIdInvalid
)) {
201 auto const lineTable
= SourceLocation::getLineTable(this);
202 return lineTable
? SourceLocation::getLineNumber(*lineTable
, pc
) : -1;
205 auto findLine
= [&] {
206 // lineMap is an atomically acquired bitwise copy of m_lineMap,
207 // with no destructor
208 auto lineMap(m_lineMap
.get());
209 if (lineMap
->empty()) return INT_MIN
;
210 auto const it
= std::upper_bound(
211 lineMap
->begin(), lineMap
->end(),
213 [] (Offset info
, const LineInfo
& elm
) {
214 return info
< elm
.first
.past
;
217 if (it
!= lineMap
->end() && it
->first
.base
<= pc
) return it
->second
;
221 auto line
= findLine();
222 if (line
!= INT_MIN
) return line
;
224 // Updating m_lineMap while coverage is enabled can cause the treadmill to
225 // fill with an enormous number of resized maps.
226 if (UNLIKELY(g_context
&& (isCoverageEnabled() || RID().getCoverage()))) {
227 return SourceLocation::getLineNumber(SourceLocation::loadLineTable(this), pc
);
230 m_lineMap
.lock_for_update();
233 if (line
!= INT_MIN
) {
238 auto const info
= SourceLocation::getLineInfo(SourceLocation::loadLineTable(this), pc
);
239 auto copy
= m_lineMap
.copy();
240 auto const it
= std::upper_bound(
241 copy
.begin(), copy
.end(),
243 [&] (const LineInfo
& a
, const LineInfo
& b
) {
244 return a
.first
.base
< b
.first
.past
;
247 assertx(it
== copy
.end() || (it
->first
.past
> pc
&& it
->first
.base
> pc
));
248 copy
.insert(it
, info
);
249 auto old
= m_lineMap
.update_and_unlock(std::move(copy
));
250 Treadmill::enqueue([old
= std::move(old
)] () mutable { old
.clear(); });
258 bool Unit::getSourceLoc(Offset pc
, SourceLoc
& sLoc
) const {
259 auto const& sourceLocTable
= SourceLocation::getLocTable(this);
260 return SourceLocation::getLoc(sourceLocTable
, pc
, sLoc
);
263 bool Unit::getOffsetRange(Offset pc
, OffsetRange
& range
) const {
264 OffsetRangeVec offsets
;
265 auto line
= getLineNumber(pc
);
266 getOffsetRanges(line
, offsets
);
268 for (auto offset
: offsets
) {
269 if (pc
>= offset
.base
&& pc
< offset
.past
) {
277 bool Unit::getOffsetRanges(int line
, OffsetRangeVec
& offsets
) const {
278 assertx(offsets
.size() == 0);
279 auto map
= SourceLocation::getLineToOffsetRangeVecMap(this);
280 auto it
= map
.find(line
);
281 if (it
== map
.end()) return false;
282 offsets
= it
->second
;
286 int Unit::getNearestLineWithCode(int line
) const {
287 auto map
= SourceLocation::getLineToOffsetRangeVecMap(this);
288 auto it
= map
.lower_bound(line
);
289 return it
== map
.end() ? -1 : it
->first
;
292 const Func
* Unit::getFunc(Offset pc
) const {
293 auto& table
= getExtended()->m_funcTable
;
294 auto it
= std::upper_bound(table
.begin(), table
.end(), nullptr,
295 [&] (const Func
* a
, const Func
* b
) {
296 assertx(a
== nullptr);
297 return pc
< b
->past();
299 if (it
!= table
.end()) {
300 assertx(pc
< (*it
)->past());
306 bool Unit::isCoverageEnabled() const {
307 return m_coverage
.bound() && m_coverage
.isInit();
309 void Unit::enableCoverage() {
310 if (!m_coverage
.bound()) {
311 assertx(!RO::RepoAuthoritative
&& RO::EvalEnablePerFileCoverage
);
314 rds::LinkName
{"UnitCoverage", filepath()}
317 if (m_coverage
.isInit()) return;
318 new (m_coverage
.get()) req::dynamic_bitset
{};
319 m_coverage
.markInit();
321 void Unit::disableCoverage() {
322 if (!isCoverageEnabled()) return;
324 m_coverage
.markUninit();
325 m_coverage
->req::dynamic_bitset::~dynamic_bitset();
327 void Unit::clearCoverage() {
328 assertx(isCoverageEnabled());
331 void Unit::recordCoverage(Offset off
) {
332 assertx(isCoverageEnabled());
334 auto const line
= getLineNumber(off
);
335 if (line
== -1) return;
337 if (m_coverage
->size() <= line
) m_coverage
->resize(line
+ 1);
338 m_coverage
->set(line
);
340 Array
Unit::reportCoverage() const {
341 assertx(isCoverageEnabled());
343 auto const& c
= *m_coverage
;
344 auto const end
= req::dynamic_bitset::npos
;
345 VecInit init
{m_coverage
->count()};
346 for (auto i
= c
.find_first(); i
!= end
; i
= c
.find_next(i
)) {
350 return init
.toArray();
352 rds::Handle
Unit::coverageDataHandle() const {
353 assertx(m_coverage
.bound());
354 return m_coverage
.handle();
357 ///////////////////////////////////////////////////////////////////////////////
358 // Funcs and PreClasses.
360 Func
* Unit::getCachedEntryPoint() const {
361 return m_cachedEntryPoint
;
364 ///////////////////////////////////////////////////////////////////////////////
367 const StaticString
s_DebuggerMain("__DebuggerMain");
369 void Unit::defFunc(Func
* func
, bool debugger
) {
370 assertx(!func
->isMethod());
371 auto const handle
= func
->funcHandle();
373 if (UNLIKELY(debugger
)) {
374 // Don't define the __debugger_main() function
375 if (func
->userAttributes().count(s_DebuggerMain
.get())) return;
378 if (rds::isPersistentHandle(handle
)) {
379 auto& funcAddr
= rds::handleToRef
<LowPtr
<Func
>,
380 rds::Mode::Persistent
>(handle
);
381 auto const oldFunc
= funcAddr
.get();
382 if (oldFunc
== func
) return;
383 if (UNLIKELY(oldFunc
!= nullptr)) {
384 assertx(oldFunc
->isBuiltin() && !func
->isBuiltin());
385 raise_error(Strings::REDECLARE_BUILTIN
, func
->name()->data());
389 assertx(rds::isNormalHandle(handle
));
390 auto& funcAddr
= rds::handleToRef
<LowPtr
<Func
>, rds::Mode::Normal
>(handle
);
391 if (!rds::isHandleInit(handle
, rds::NormalTag
{})) {
392 rds::initHandle(handle
);
394 if (funcAddr
.get() == func
) return;
395 if (func
->attrs() & AttrIsMethCaller
) {
396 // emit the duplicated meth_caller directly
399 raise_error(Strings::FUNCTION_ALREADY_DEFINED
, func
->name()->data());
404 if (func
->isUnique()) func
->getNamedEntity()->setUniqueFunc(func
);
406 if (UNLIKELY(debugger
)) phpDebuggerDefFuncHook(func
);
409 Func
* Unit::lookupFunc(const NamedEntity
* ne
) {
410 return ne
->getCachedFunc();
413 Func
* Unit::lookupFunc(const StringData
* name
) {
414 const NamedEntity
* ne
= NamedEntity::get(name
);
415 return ne
->getCachedFunc();
418 Func
* Unit::lookupBuiltin(const StringData
* name
) {
419 // Builtins are either persistent (the normal case), or defined at the
420 // beginning of every request (if JitEnableRenameFunction or interception is
421 // enabled). In either case, they're unique, so they should be present in the
423 auto const ne
= NamedEntity::get(name
);
424 auto const f
= ne
->uniqueFunc();
425 return (f
&& f
->isBuiltin()) ? f
: nullptr;
428 Func
* Unit::loadFunc(const NamedEntity
* ne
, const StringData
* name
) {
429 Func
* func
= ne
->getCachedFunc();
430 if (LIKELY(func
!= nullptr)) return func
;
431 if (AutoloadHandler::s_instance
->autoloadFunc(
432 const_cast<StringData
*>(name
))) {
433 func
= ne
->getCachedFunc();
438 Func
* Unit::loadFunc(const StringData
* name
) {
440 auto ne
= NamedEntity::get(name
, true, &normStr
);
442 // Try to fetch from cache
443 Func
* func_
= ne
->getCachedFunc();
444 if (LIKELY(func_
!= nullptr)) return func_
;
446 // Normalize the namespace
448 name
= normStr
.get();
451 // Autoload the function
452 return AutoloadHandler::s_instance
->autoloadFunc(
453 const_cast<StringData
*>(name
)
454 ) ? ne
->getCachedFunc() : nullptr;
457 void Unit::bindFunc(Func
*func
) {
458 assertx(!func
->isMethod());
459 auto const ne
= func
->getNamedEntity();
461 auto const persistent
=
462 (RuntimeOption::RepoAuthoritative
|| !SystemLib::s_inited
) &&
463 (func
->attrs() & AttrPersistent
);
465 auto const init_val
= LowPtr
<Func
>(func
);
467 ne
->m_cachedFunc
.bind(
468 persistent
? rds::Mode::Persistent
: rds::Mode::Normal
,
469 rds::LinkName
{"Func", func
->name()},
472 if (func
->isUnique() && func
== ne
->getCachedFunc()) {
473 // we need to check that we actually were responsible for the bind here
474 // before we set the uniqueFunc on `ne`. this seems strange, but it's
475 // because meth_caller funcs are unique but can have the same name.
476 ne
->setUniqueFunc(func
);
478 func
->setFuncHandle(ne
->m_cachedFunc
);
481 ///////////////////////////////////////////////////////////////////////////////
482 // Class lookup utilities
485 struct FrameRestore
: private VMRegAnchor
{
486 explicit FrameRestore(const PreClass
* preClass
) :
487 FrameRestore(VMParserFrame
{ preClass
->unit()->filepath(), preClass
->line1() }) {}
489 explicit FrameRestore(const PreRecordDesc
* pr
) :
490 FrameRestore(VMParserFrame
{ pr
->unit()->filepath(), pr
->line1() }) {}
492 explicit FrameRestore(const PreTypeAlias
* ta
) :
493 FrameRestore(VMParserFrame
{ ta
->unit
->filepath(), ta
->line1
}) {}
495 explicit NEVER_INLINE
FrameRestore(const VMParserFrame
& parserframe
) :
496 m_parserframe(parserframe
) {
497 m_oldParserframe
= BacktraceArgs::setGlobalParserFrame(&m_parserframe
);
501 BacktraceArgs::setGlobalParserFrame(m_oldParserframe
);
505 VMParserFrame m_parserframe
;
506 VMParserFrame
* m_oldParserframe
;
510 const char* checkSameName(NamedEntity
* nameList
) {
511 if (!std::is_same
<T
, PreTypeAlias
>::value
&& nameList
->getCachedTypeAlias()) {
513 } else if (!std::is_same
<T
, RecordDesc
>::value
&&
514 nameList
->getCachedRecordDesc()) {
516 } else if (!std::is_same
<T
, PreClass
>::value
&& nameList
->getCachedClass()) {
522 void setupRecord(RecordDesc
* newRecord
, NamedEntity
* nameList
) {
523 bool const isPersistent
=
524 (!SystemLib::s_inited
|| RuntimeOption::RepoAuthoritative
) &&
525 newRecord
->verifyPersistent();
526 nameList
->m_cachedRecordDesc
.bind(
527 isPersistent
? rds::Mode::Persistent
: rds::Mode::Normal
,
528 rds::LinkName
{"NERecord", newRecord
->name()}
530 newRecord
->setRecordDescHandle(nameList
->m_cachedRecordDesc
);
531 newRecord
->incAtomicCount();
532 nameList
->pushRecordDesc(newRecord
);
535 void setupClass(Class
* newClass
, NamedEntity
* nameList
) {
536 bool const isPersistent
=
537 (!SystemLib::s_inited
|| RuntimeOption::RepoAuthoritative
) &&
538 newClass
->verifyPersistent();
539 nameList
->m_cachedClass
.bind(
540 isPersistent
? rds::Mode::Persistent
: rds::Mode::Normal
,
541 rds::LinkName
{"NEClass", newClass
->name()}
544 if (newClass
->isBuiltin()) {
545 assertx(newClass
->isUnique());
546 for (auto i
= newClass
->numMethods(); i
--;) {
547 auto const func
= newClass
->getMethod(i
);
548 if (func
->isCPPBuiltin() && func
->isStatic()) {
549 assertx(func
->isUnique());
550 NamedEntity::get(func
->fullName())->setUniqueFunc(func
);
555 newClass
->setClassHandle(nameList
->m_cachedClass
);
556 newClass
->incAtomicCount();
558 InstanceBits::ifInitElse(
559 [&] { newClass
->setInstanceBits();
560 nameList
->pushClass(newClass
); },
561 [&] { nameList
->pushClass(newClass
); }
564 if (RuntimeOption::EvalEnableReverseDataMap
) {
565 // The corresponding deregister is in NamedEntity::removeClass().
566 data_map::register_start(newClass
);
571 ///////////////////////////////////////////////////////////////////////////////
574 Class
* Unit::defClass(const PreClass
* preClass
,
575 bool failIsFatal
/* = true */) {
576 FTRACE(3, " Defining cls {} failIsFatal {}\n",
577 preClass
->name()->data(), failIsFatal
);
578 NamedEntity
* const nameList
= preClass
->namedEntity();
579 Class
* top
= nameList
->clsList();
582 * Check if there is already a name defined in this request for this
585 * Raise a fatal unless the existing class definition is identical to the
586 * one this invocation would create.
588 auto existingKind
= checkSameName
<PreClass
>(nameList
);
590 FrameRestore
fr(preClass
);
591 raise_error("Cannot declare class with the same name (%s) as an "
592 "existing %s", preClass
->name()->data(), existingKind
);
596 // If there was already a class declared with DefClass, check if it's
598 if (Class
* cls
= nameList
->getCachedClass()) {
599 if (cls
->preClass() != preClass
) {
601 FrameRestore
fr(preClass
);
602 raise_error("Class already declared: %s", preClass
->name()->data());
606 assertx(!RO::RepoAuthoritative
||
607 (cls
->isPersistent() && classHasPersistentRDS(cls
)));
611 // Get a compatible Class, and add it to the list of defined classes.
612 Class
* parent
= nullptr;
614 // Search for a compatible extant class. Searching from most to least
615 // recently created may have better locality than alternative search orders.
616 // In addition, its the only simple way to make this work lock free...
617 for (Class
* class_
= top
; class_
!= nullptr; ) {
619 class_
= class_
->m_next
;
620 if (cur
->preClass() != preClass
) continue;
621 Class::Avail avail
= cur
->avail(parent
, failIsFatal
/*tryAutoload*/);
622 if (LIKELY(avail
== Class::Avail::True
)) {
624 DEBUGGER_ATTACHED_ONLY(phpDebuggerDefClassHook(cur
));
625 assertx(!RO::RepoAuthoritative
||
626 (cur
->isPersistent() && classHasPersistentRDS(cur
)));
629 if (avail
== Class::Avail::Fail
) {
631 FrameRestore
fr(preClass
);
632 raise_error("unknown class %s", parent
->name()->data());
636 assertx(avail
== Class::Avail::False
);
639 if (!parent
&& preClass
->parent()->size() != 0) {
640 parent
= Unit::getClass(preClass
->parent(), failIsFatal
);
641 if (parent
== nullptr) {
643 FrameRestore
fr(preClass
);
644 raise_error("unknown class %s", preClass
->parent()->data());
652 for (auto it
= preClass
->interfaces().begin();
653 it
!= preClass
->interfaces().end(); ++it
) {
654 if (!Unit::getClass(*it
, false)) return nullptr;
657 for (auto const& traitName
: preClass
->usedTraits()) {
658 if (!Unit::getClass(traitName
, false)) return nullptr;
661 if (preClass
->attrs() & AttrEnum
) {
662 auto const enumBaseTy
=
663 preClass
->enumBaseTy().underlyingDataTypeResolved();
665 (!isIntType(*enumBaseTy
) && !isStringType(*enumBaseTy
))) {
671 // Create a new class.
674 FrameRestore
fr(preClass
);
675 newClass
= Class::newClass(const_cast<PreClass
*>(preClass
), parent
);
677 Lock
l(g_classesMutex
);
679 if (UNLIKELY(top
!= nameList
->clsList())) {
680 top
= nameList
->clsList();
684 setupClass(newClass
.get(), nameList
);
687 * call setCached after adding to the class list, otherwise the
688 * target-cache short circuit at the top could return a class
689 * which is not yet on the clsList().
691 newClass
.get()->setCached();
692 DEBUGGER_ATTACHED_ONLY(phpDebuggerDefClassHook(newClass
.get()));
693 assertx(!RO::RepoAuthoritative
||
694 (newClass
.get()->isPersistent() &&
695 classHasPersistentRDS(newClass
.get())));
696 return newClass
.get();
700 Class
* Unit::defClosure(const PreClass
* preClass
) {
701 auto const nameList
= preClass
->namedEntity();
703 if (nameList
->clsList()) return nameList
->clsList();
705 auto const parent
= c_Closure::classof();
707 assertx(preClass
->parent() == parent
->name());
708 // Create a new class.
711 Class::newClass(const_cast<PreClass
*>(preClass
), parent
)
714 Lock
l(g_classesMutex
);
716 if (UNLIKELY(nameList
->clsList() != nullptr)) return nameList
->clsList();
718 setupClass(newClass
.get(), nameList
);
720 if (classHasPersistentRDS(newClass
.get())) newClass
.get()->setCached();
721 return newClass
.get();
724 Class
* Unit::loadClass(const NamedEntity
* ne
,
725 const StringData
* name
) {
727 if (LIKELY((cls
= ne
->getCachedClass()) != nullptr)) {
730 return loadMissingClass(ne
, name
);
733 Class
* Unit::loadMissingClass(const NamedEntity
* ne
,
734 const StringData
* name
) {
736 AutoloadHandler::s_instance
->autoloadClass(
737 StrNR(const_cast<StringData
*>(name
)));
738 return Unit::lookupClass(ne
);
741 Class
* Unit::getClass(const NamedEntity
* ne
,
742 const StringData
*name
, bool tryAutoload
) {
743 Class
*cls
= lookupClass(ne
);
744 if (UNLIKELY(!cls
)) {
746 return loadMissingClass(ne
, name
);
752 bool Unit::classExists(const StringData
* name
, bool autoload
, ClassKind kind
) {
753 Class
* cls
= Unit::getClass(name
, autoload
);
755 (cls
->attrs() & (AttrInterface
| AttrTrait
)) == classKindAsAttr(kind
);
758 ///////////////////////////////////////////////////////////////////////////////
759 // RecordDesc lookup.
761 RecordDesc
* Unit::defRecordDesc(PreRecordDesc
* preRecord
,
762 bool failIsFatal
/* = true */) {
763 auto const nameList
= preRecord
->namedEntity();
765 // Error out if there is already a different type
766 // with the same name in the request
767 auto existingKind
= checkSameName
<RecordDesc
>(nameList
);
769 FrameRestore
fr(preRecord
);
770 raise_error("Cannot declare record with the same (%s) as an "
771 "existing %s", preRecord
->name()->data(), existingKind
);
775 // If there was already a record declared with DefRecordDesc, check if it's
777 if (auto cachedRec
= nameList
->getCachedRecordDesc()) {
778 if (cachedRec
->preRecordDesc() != preRecord
) {
780 FrameRestore
fr(preRecord
);
781 raise_error("Record already declared: %s", preRecord
->name()->data());
788 // Get a compatible predefined record, if one exists. Otherwise, add the
789 // current one to the list of defined records.
790 // Set the cached record in either case.
791 RecordDesc
* parent
= nullptr;
792 auto top
= nameList
->recordList();
794 for (auto rec
= top
; rec
!= nullptr; rec
= rec
->m_next
) {
795 if (rec
->preRecordDesc() != preRecord
) continue;
796 auto avail
= rec
->availWithParent(parent
, failIsFatal
/*tryAutoload*/);
797 if (LIKELY(avail
== RecordDesc::Avail::True
)) {
801 if (avail
== RecordDesc::Avail::Fail
) {
802 // parent is not available and cannot be autoloaded
804 FrameRestore
fr(preRecord
);
805 raise_error("unknown record %s", parent
->name()->data());
809 assertx(avail
== RecordDesc::Avail::False
);
812 // Create a new record.
813 if (!parent
&& preRecord
->parentName()->size() != 0) {
815 parent
= Unit::getRecordDesc(preRecord
->parentName(), failIsFatal
);
816 if (parent
== nullptr) {
818 FrameRestore
fr(preRecord
);
819 raise_error("unknown record %s", preRecord
->parentName()->data());
825 RecordDescPtr newRecord
;
827 FrameRestore
fr(preRecord
);
828 newRecord
= RecordDesc::newRecordDesc(
829 const_cast<PreRecordDesc
*>(preRecord
), parent
);
832 Lock
l(g_recordsMutex
);
834 if (UNLIKELY(top
!= nameList
->recordList())) {
835 top
= nameList
->recordList();
839 setupRecord(newRecord
.get(), nameList
);
841 newRecord
->setCached();
842 return newRecord
.get();
846 RecordDesc
* Unit::loadMissingRecordDesc(const NamedEntity
* ne
,
847 const StringData
* name
) {
849 AutoloadHandler::s_instance
->autoloadRecordDesc(
850 StrNR(const_cast<StringData
*>(name
)));
851 return Unit::lookupRecordDesc(ne
);
854 RecordDesc
* Unit::getRecordDesc(const StringData
* name
, bool tryAutoload
) {
856 auto ne
= NamedEntity::get(name
, true, &normStr
);
858 name
= normStr
.get();
860 return getRecordDesc(ne
, name
, tryAutoload
);
863 RecordDesc
* Unit::getRecordDesc(const NamedEntity
* ne
,
864 const StringData
*name
, bool tryAutoload
) {
865 RecordDesc
*rec
= lookupRecordDesc(ne
);
866 if (UNLIKELY(!rec
)) {
868 return loadMissingRecordDesc(ne
, name
);
874 ///////////////////////////////////////////////////////////////////////////////
877 TypedValue
Unit::lookupCns(const StringData
* cnsName
) {
878 auto const handle
= lookupCnsHandle(cnsName
);
880 if (LIKELY(rds::isHandleBound(handle
) &&
881 rds::isHandleInit(handle
))) {
882 auto const& tv
= rds::handleToRef
<TypedValue
, rds::Mode::NonLocal
>(handle
);
884 if (LIKELY(type(tv
) != KindOfUninit
)) {
885 assertx(tvIsPlausible(tv
));
890 assertx(tv
.m_data
.pcnt
!= nullptr);
891 auto const callback
=
892 reinterpret_cast<Native::ConstantCallback
>(tv
.m_data
.pcnt
);
893 Variant v
= callback(cnsName
);
894 const TypedValue tvRet
= v
.detach();
895 assertx(tvIsPlausible(tvRet
));
896 assertx(tvAsCVarRef(&tvRet
).isAllowedAsConstantValue());
898 if (rds::isNormalHandle(handle
) && type(tvRet
) != KindOfResource
) {
900 rds::handleToRef
<TypedValue
, rds::Mode::Normal
>(handle
) = tvRet
;
904 return make_tv
<KindOfUninit
>();
907 const TypedValue
* Unit::lookupPersistentCns(const StringData
* cnsName
) {
908 auto const handle
= lookupCnsHandle(cnsName
);
909 if (!rds::isHandleBound(handle
) || !rds::isPersistentHandle(handle
)) {
912 auto const ret
= rds::handleToPtr
<TypedValue
, rds::Mode::Persistent
>(handle
);
913 assertx(tvIsPlausible(*ret
));
917 TypedValue
Unit::loadCns(const StringData
* cnsName
) {
918 auto const tv
= lookupCns(cnsName
);
919 if (LIKELY(type(tv
) != KindOfUninit
)) return tv
;
921 if (needsNSNormalization(cnsName
)) {
922 return loadCns(normalizeNS(cnsName
));
925 if (!AutoloadHandler::s_instance
->autoloadConstant(
926 const_cast<StringData
*>(cnsName
))) {
927 return make_tv
<KindOfUninit
>();
929 return lookupCns(cnsName
);
932 Variant
Unit::getCns(const StringData
* name
) {
933 const StringData
* func_name
= Constant::funcNameFromName(name
);
934 Func
* func
= Unit::lookupFunc(func_name
);
937 "The function should have been autoloaded when we loaded the constant");
938 return Variant::attach(
939 g_context
->invokeFuncFew(func
, nullptr, 0, nullptr, false, false)
943 void Unit::defCns(Id id
) {
944 assertx(id
< m_constants
.size());
945 auto constant
= &m_constants
[id
];
946 auto const cnsName
= constant
->name
;
947 FTRACE(3, " Defining def {}\n", cnsName
->data());
948 auto const cnsVal
= constant
->val
;
950 if (constant
->attrs
& Attr::AttrPersistent
&&
951 bindPersistentCns(cnsName
, cnsVal
)) {
955 auto const ch
= makeCnsHandle(cnsName
);
956 assertx(rds::isHandleBound(ch
));
957 auto cns
= rds::handleToPtr
<TypedValue
, rds::Mode::NonLocal
>(ch
);
959 if (!rds::isHandleInit(ch
)) {
960 cns
->m_type
= KindOfUninit
;
961 cns
->m_data
.pcnt
= nullptr;
964 if (UNLIKELY(cns
->m_type
!= KindOfUninit
||
965 cns
->m_data
.pcnt
!= nullptr)) {
966 raise_error(Strings::CONSTANT_ALREADY_DEFINED
, cnsName
->data());
969 assertx(tvAsCVarRef(&cnsVal
).isAllowedAsConstantValue() ||
970 (cnsVal
.m_type
== KindOfUninit
&&
971 cnsVal
.m_data
.pcnt
!= nullptr));
973 assertx(rds::isNormalHandle(ch
));
978 bool Unit::defNativeConstantCallback(const StringData
* cnsName
,
980 static const bool kServer
= RuntimeOption::ServerExecutionMode();
981 // Zend doesn't define the STD* streams in server mode so we don't either
982 if (UNLIKELY(kServer
&&
983 (s_stdin
.equal(cnsName
) ||
984 s_stdout
.equal(cnsName
) ||
985 s_stderr
.equal(cnsName
)))) {
988 bindPersistentCns(cnsName
, value
);
992 ///////////////////////////////////////////////////////////////////////////////
997 TypeAlias
typeAliasFromRecordDesc(const PreTypeAlias
* thisType
,
1000 req
.unit
= thisType
->unit
;
1001 req
.name
= thisType
->name
;
1002 req
.nullable
= thisType
->nullable
;
1003 req
.type
= AnnotType::Record
;
1005 req
.userAttrs
= thisType
->userAttrs
;
1006 assertx(thisType
->typeStructure
.isHAMSafeDArray());
1007 req
.typeStructure
= thisType
->typeStructure
;
1011 TypeAlias
typeAliasFromClass(const PreTypeAlias
* thisType
,
1014 req
.unit
= thisType
->unit
;
1015 req
.name
= thisType
->name
;
1016 req
.nullable
= thisType
->nullable
;
1017 if (isEnum(klass
)) {
1018 // If the class is an enum, pull out the actual base type.
1019 if (auto const enumType
= klass
->enumBaseTy()) {
1020 req
.type
= enumDataTypeToAnnotType(*enumType
);
1022 req
.type
= AnnotType::ArrayKey
;
1025 req
.type
= AnnotType::Object
;
1028 req
.userAttrs
= thisType
->userAttrs
;
1029 assertx(thisType
->typeStructure
.isHAMSafeDArray());
1030 req
.typeStructure
= thisType
->typeStructure
;
1034 TypeAlias
resolveTypeAlias(Unit
* unit
, const PreTypeAlias
* thisType
) {
1036 * If this type alias is a KindOfObject and the name on the right
1037 * hand side was another type alias, we will bind the name to the
1038 * other side for this request (i.e. resolve that type alias now).
1040 * We need to inspect the right hand side and figure out what it was
1043 * If the right hand side was a class, we need to autoload and
1044 * ensure it exists at this point.
1046 if (thisType
->type
!= AnnotType::Object
) {
1047 return TypeAlias::From(*thisType
);
1051 * If the right hand side is already defined, don't invoke the
1052 * autoloader at all, this means we have to check for both a type
1053 * alias and a class before attempting to load them via the
1056 * While normal autoloaders are fine, the "failure" entry in the
1057 * map passed to `HH\set_autoload_paths` is called for all failed
1058 * lookups. The failure function can do anything, including something
1059 * like like raising an error or rebuilding the map. We don't want to
1060 * send speculative (or worse, repeat) requests to the autoloader, so
1061 * do our due diligence here.
1064 const StringData
* typeName
= thisType
->value
;
1065 auto targetNE
= NamedEntity::get(typeName
);
1067 if (auto klass
= Unit::lookupClass(targetNE
)) {
1068 return typeAliasFromClass(thisType
, klass
);
1071 if (auto targetTd
= targetNE
->getCachedTypeAlias()) {
1072 return TypeAlias::From(*targetTd
, *thisType
);
1075 if (auto rec
= Unit::lookupRecordDesc(targetNE
)) {
1076 return typeAliasFromRecordDesc(thisType
, rec
);
1079 if (AutoloadHandler::s_instance
->autoloadNamedType(
1080 StrNR(const_cast<StringData
*>(typeName
))
1082 if (auto klass
= Unit::lookupClass(targetNE
)) {
1083 return typeAliasFromClass(thisType
, klass
);
1085 if (auto targetTd
= targetNE
->getCachedTypeAlias()) {
1086 return TypeAlias::From(*targetTd
, *thisType
);
1088 if (auto rec
= Unit::lookupRecordDesc(targetNE
)) {
1089 return typeAliasFromRecordDesc(thisType
, rec
);
1093 return TypeAlias::Invalid(*thisType
);
1096 ///////////////////////////////////////////////////////////////////////////////
1099 const TypeAlias
* Unit::lookupTypeAlias(const StringData
* name
,
1101 auto ne
= NamedEntity::get(name
);
1102 auto target
= ne
->getCachedTypeAlias();
1103 if (persistent
) *persistent
= ne
->isPersistentTypeAlias();
1107 const TypeAlias
* Unit::loadTypeAlias(const StringData
* name
,
1109 auto ne
= NamedEntity::get(name
);
1110 auto target
= ne
->getCachedTypeAlias();
1112 if (AutoloadHandler::s_instance
->autoloadNamedType(
1113 StrNR(const_cast<StringData
*>(name
))
1115 target
= ne
->getCachedTypeAlias();
1121 if (persistent
) *persistent
= ne
->isPersistentTypeAlias();
1125 Unit::DefTypeAliasResult
Unit::defTypeAlias(Id id
, bool failIsFatal
) {
1126 assertx(id
< m_typeAliases
.size());
1127 auto thisType
= &m_typeAliases
[id
];
1128 FTRACE(3, " Defining type alias {}\n", thisType
->name
->data());
1129 auto nameList
= NamedEntity::get(thisType
->name
);
1130 const StringData
* typeName
= thisType
->value
;
1133 * Check if this name already was defined as a type alias, and if so
1134 * make sure it is compatible.
1136 if (auto current
= nameList
->getCachedTypeAlias()) {
1137 auto raiseIncompatible
= [&] {
1138 FrameRestore
_(thisType
);
1139 raise_error("The type %s is already defined to an incompatible type",
1140 thisType
->name
->data());
1142 if (nameList
->isPersistentTypeAlias()) {
1143 // We may have cached the fully resolved type in a previous request.
1144 if (resolveTypeAlias(this, thisType
) != *current
) {
1145 if (!failIsFatal
) return Unit::DefTypeAliasResult::Fail
;
1146 raiseIncompatible();
1148 return Unit::DefTypeAliasResult::Persistent
;
1150 if (!current
->compat(*thisType
)) {
1151 if (!failIsFatal
) return Unit::DefTypeAliasResult::Fail
;
1152 raiseIncompatible();
1154 assertx(!RO::RepoAuthoritative
);
1155 return Unit::DefTypeAliasResult::Normal
;
1158 // There might also be a class or record with this name already.
1159 auto existingKind
= checkSameName
<PreTypeAlias
>(nameList
);
1161 if (!failIsFatal
) return Unit::DefTypeAliasResult::Fail
;
1162 FrameRestore
_(thisType
);
1163 raise_error("The name %s is already defined as a %s",
1164 thisType
->name
->data(), existingKind
);
1168 auto resolved
= resolveTypeAlias(this, thisType
);
1169 if (resolved
.invalid
) {
1170 if (!failIsFatal
) return Unit::DefTypeAliasResult::Fail
;
1171 FrameRestore
_(thisType
);
1172 raise_error("Unknown type or class %s", typeName
->data());
1176 auto const persistent
= (thisType
->attrs
& AttrPersistent
) &&
1177 (!resolved
.klass
|| classHasPersistentRDS(resolved
.klass
)) &&
1178 (!resolved
.rec
|| recordHasPersistentRDS(resolved
.rec
));
1180 nameList
->m_cachedTypeAlias
.bind(
1181 persistent
? rds::Mode::Persistent
: rds::Mode::Normal
,
1182 rds::LinkName
{"TypeAlias", thisType
->name
},
1185 if (nameList
->m_cachedTypeAlias
.isPersistent()) {
1186 return Unit::DefTypeAliasResult::Persistent
;
1189 nameList
->setCachedTypeAlias(resolved
);
1190 return Unit::DefTypeAliasResult::Normal
;
1193 ///////////////////////////////////////////////////////////////////////////////
1197 ///////////////////////////////////////////////////////////////////////////////
1199 SimpleMutex
unitInitLock(false /* reentrant */, RankUnitInit
);
1200 std::atomic
<uint64_t> s_loadedUnits
{0};
1202 void setGlobal(StringData
* name
, TypedValue
*value
) {
1203 g_context
->m_globalNVTable
->set(name
, value
);
1207 * count the number of the EntryPoint in a file, and return a iterator
1208 * 1) there is not EntryPoint, return the begin()
1209 * 2) there are multiple EntryPoints, return the end()
1210 * 3) there is exact one EntryPoint, return that iterator points to the location
1212 Func
* findEntryPoint(const Unit
* unit
) {
1213 auto it
= unit
->funcs().begin();
1216 auto EntryPointTag
= makeStaticString("__EntryPoint");
1217 for (; it
!= unit
->funcs().end(); it
++) {
1218 if ((*it
)->userAttributes().count(EntryPointTag
)) {
1221 folly::sformat("There are multiple entryPoint in {}",
1222 unit
->filepath()->data()).c_str()
1229 if (found
) return *retIt
;
1234 bool isEvalName(const StringData
* name
) {
1235 return name
->empty() || boost::ends_with(name
->slice(), EVAL_FILENAME_SUFFIX
);
1238 ///////////////////////////////////////////////////////////////////////////////
1241 void Unit::initialMerge() {
1242 unitInitLock
.assertOwnedBySelf();
1243 if (m_mergeState
.load(std::memory_order_relaxed
) != MergeState::Unmerged
) {
1247 auto const nrecord
= RuntimeOption::EvalRecordFirstUnits
;
1248 if (s_loadedUnits
.load(std::memory_order_relaxed
) < nrecord
) {
1249 auto const index
= s_loadedUnits
.fetch_add(1, std::memory_order_relaxed
);
1250 if (index
< nrecord
) {
1251 StructuredLogEntry ent
;
1252 ent
.setStr("path", m_filepath
->data());
1253 ent
.setInt("index", index
);
1254 StructuredLog::log("hhvm_first_units", ent
);
1258 this->m_cachedEntryPoint
= findEntryPoint(this);
1260 if (RuntimeOption::EvalEnableReverseDataMap
) {
1261 data_map::register_start(this);
1265 bool needsCompact
= false;
1266 m_mergeState
.store(MergeState::Merging
, std::memory_order_relaxed
);
1268 auto const mi
= m_mergeInfo
.load(std::memory_order_relaxed
);
1269 bool allFuncsUnique
= RuntimeOption::RepoAuthoritative
;
1270 for (auto& func
: mi
->mutableFuncs()) {
1271 if (allFuncsUnique
) {
1272 allFuncsUnique
= (func
->attrs() & AttrUnique
);
1275 if (rds::isPersistentHandle(func
->funcHandle())) {
1276 needsCompact
= true;
1279 if (allFuncsUnique
) state
|= MergeState::UniqueFuncs
;
1281 if (RuntimeOption::RepoAuthoritative
|| !SystemLib::s_inited
) {
1283 * The mergeables array begins with the hoistable Func*s,
1284 * followed by the (potentially) hoistable Class*s.
1286 * If the Unit is merge only, it then contains enough information
1287 * to simulate executing the pseudomain. Normally, this is just
1288 * the Class*s that might not be hoistable. In RepoAuthoritative
1289 * mode it also includes assignments of the form:
1290 * $GLOBALS[string-literal] = scalar;
1291 * defines of the form:
1292 * define(string-literal, scalar);
1295 * These cases are differentiated using the bottom 3 bits
1296 * of the pointer. In the case of a define or a global,
1297 * the pointer will be followed by a TypedValue representing
1298 * the value being defined/assigned.
1300 int ix
= mi
->m_firstHoistablePreClass
;
1301 int end
= mi
->m_firstMergeablePreClass
;
1303 PreClass
* pre
= (PreClass
*)mi
->mergeableObj(ix
++);
1304 if (pre
->attrs() & AttrUnique
) {
1305 needsCompact
= true;
1309 ix
= mi
->m_firstMergeablePreClass
;
1310 end
= mi
->m_mergeablesSize
;
1312 void *obj
= mi
->mergeableObj(ix
);
1313 auto k
= MergeKind(uintptr_t(obj
) & 7);
1315 case MergeKind::UniqueDefinedClass
:
1316 case MergeKind::Done
:
1318 case MergeKind::TypeAlias
: {
1319 auto const aliasId
= static_cast<Id
>(intptr_t(obj
)) >> 3;
1320 if (m_typeAliases
[aliasId
].attrs
& AttrPersistent
) {
1321 needsCompact
= true;
1325 case MergeKind::Class
:
1326 if (static_cast<PreClass
*>(obj
)->attrs() & AttrUnique
) {
1327 needsCompact
= true;
1330 case MergeKind::Record
:
1332 case MergeKind::Define
: {
1333 auto const constantId
= static_cast<Id
>(intptr_t(obj
)) >> 3;
1334 if (m_constants
[constantId
].attrs
& AttrPersistent
) {
1335 needsCompact
= true;
1342 if (needsCompact
) state
|= MergeState::NeedsCompact
;
1345 if (!RO::RepoAuthoritative
&& RO::EvalEnablePerFileCoverage
) {
1348 rds::LinkName
{"UnitCoverage", filepath()}
1351 m_mergeState
.store(MergeState::Merged
| state
, std::memory_order_relaxed
);
1354 void Unit::merge() {
1355 ARRPROV_USE_RUNTIME_LOCATION();
1356 if (m_mergeState
.load(std::memory_order_relaxed
) & MergeState::Empty
) {
1359 if (UNLIKELY(!(m_mergeState
.load(std::memory_order_relaxed
) &
1360 MergeState::Merged
))) {
1361 SimpleLock
lock(unitInitLock
);
1366 raise_parse_error(filepath(),
1367 m_fatalInfo
->m_fatalMsg
.c_str(),
1368 m_fatalInfo
->m_fatalLoc
);
1371 if (UNLIKELY(isDebuggerAttached())) {
1372 mergeImpl
<true>(mergeInfo());
1374 mergeImpl
<false>(mergeInfo());
1378 static size_t compactMergeInfo(Unit::MergeInfo
* in
, Unit::MergeInfo
* out
,
1379 const Unit::TypeAliasVec
& aliasInfo
,
1380 const Unit::ConstantVec
& constantInfo
) {
1381 using MergeKind
= Unit::MergeKind
;
1383 Func
** it
= in
->funcBegin();
1384 Func
** fend
= in
->funcEnd();
1385 Func
** iout
= nullptr;
1386 unsigned ix
, end
, oix
= 0;
1389 if (in
!= out
) memcpy(out
, in
, uintptr_t(it
) - uintptr_t(in
));
1390 iout
= out
->funcBegin();
1394 while (it
!= fend
) {
1396 if (rds::isPersistentHandle(func
->funcHandle())) {
1404 oix
= out
->m_firstHoistablePreClass
-= delta
;
1407 ix
= in
->m_firstHoistablePreClass
;
1408 end
= in
->m_firstMergeablePreClass
;
1409 for (; ix
< end
; ++ix
) {
1410 void* obj
= in
->mergeableObj(ix
);
1411 assertx((uintptr_t(obj
) & 1) == 0);
1412 PreClass
* pre
= (PreClass
*)obj
;
1413 if (pre
->attrs() & AttrUnique
) {
1414 Class
* cls
= pre
->namedEntity()->clsList();
1415 assertx(cls
&& !cls
->m_next
);
1416 assertx(cls
->preClass() == pre
);
1417 if (rds::isPersistentHandle(cls
->classHandle())) {
1420 out
->mergeableObj(oix
++) = (void*)(uintptr_t(cls
) | 1);
1423 out
->mergeableObj(oix
++) = obj
;
1428 out
->m_firstMergeablePreClass
= oix
;
1431 end
= in
->m_mergeablesSize
;
1433 void* obj
= in
->mergeableObj(ix
++);
1434 auto k
= MergeKind(uintptr_t(obj
) & 7);
1436 case MergeKind::Class
: {
1437 PreClass
* pre
= (PreClass
*)obj
;
1438 if (pre
->attrs() & AttrUnique
) {
1439 Class
* cls
= pre
->namedEntity()->clsList();
1440 assertx(cls
&& !cls
->m_next
);
1441 assertx(cls
->preClass() == pre
);
1442 if (rds::isPersistentHandle(cls
->classHandle())) {
1445 out
->mergeableObj(oix
++) = (void*)
1446 (uintptr_t(cls
) | uintptr_t(MergeKind::UniqueDefinedClass
));
1449 out
->mergeableObj(oix
++) = obj
;
1453 case MergeKind::Record
: {
1454 if (out
) out
->mergeableObj(oix
++) = obj
;
1457 case MergeKind::TypeAlias
: {
1458 auto const aliasId
= static_cast<Id
>(intptr_t(obj
)) >> 3;
1459 if (aliasInfo
[aliasId
].attrs
& AttrPersistent
) {
1462 out
->mergeableObj(oix
++) = obj
;
1466 case MergeKind::UniqueDefinedClass
:
1468 case MergeKind::Define
: {
1469 auto const constantId
= static_cast<Id
>(intptr_t(obj
)) >> 3;
1470 if (constantInfo
[constantId
].attrs
& AttrPersistent
) {
1473 out
->mergeableObj(oix
++) = obj
;
1478 case MergeKind::Done
:
1483 // copy the MergeKind::Done marker
1484 out
->mergeableObj(oix
) = in
->mergeableObj(ix
);
1485 out
->m_mergeablesSize
= oix
;
1490 template <bool debugger
>
1491 void Unit::mergeImpl(MergeInfo
* mi
) {
1492 assertx(m_mergeState
.load(std::memory_order_relaxed
) & MergeState::Merged
);
1493 autoTypecheck(this);
1495 FTRACE(1, "Merging unit {} ({} elements to define)\n",
1496 this->m_filepath
->data(), mi
->m_mergeablesSize
);
1498 Func
** it
= mi
->funcBegin();
1499 Func
** fend
= mi
->funcEnd();
1501 if (LIKELY((m_mergeState
.load(std::memory_order_relaxed
) &
1502 MergeState::UniqueFuncs
) != 0)) {
1505 assertx(func
->isUnique());
1506 auto const handle
= func
->funcHandle();
1507 if (rds::isNormalHandle(handle
)) {
1508 rds::handleToRef
<LowPtr
<Func
>, rds::Mode::Normal
>(handle
) = func
;
1509 rds::initHandle(handle
);
1511 assertx(rds::isPersistentHandle(handle
));
1512 rds::handleToRef
<LowPtr
<Func
>, rds::Mode::Persistent
>(handle
) = func
;
1515 auto const ne
= func
->getNamedEntity();
1516 auto const f
= ne
->uniqueFunc();
1517 if (f
&& f
->attrs() & AttrIsMethCaller
) {
1518 // Skip the duplicated meth_caller
1521 ne
->setUniqueFunc(func
);
1522 if (debugger
) phpDebuggerDefFuncHook(func
);
1523 } while (++it
!= fend
);
1527 defFunc(func
, debugger
);
1528 } while (++it
!= fend
);
1532 int first
= mi
->m_firstHoistablePreClass
;
1533 int end
= mi
->m_firstMergeablePreClass
;
1537 FTRACE(3, "ix: {}, end: {}, total: {}\n", ix
, end
, mi
->m_mergeablesSize
);
1539 boost::dynamic_bitset
<> define(mi
->m_mergeablesSize
- first
);
1540 // iterate over all the potentially hoistable classes
1541 // with no fatals on failure
1542 for (; ix
< end
; ++ix
) {
1543 // The first time this unit is merged, if the classes turn out to be all
1544 // unique and defined, we replace the PreClass*'s with the corresponding
1545 // Class*'s, with the low-order bit marked.
1546 PreClass
* pre
= (PreClass
*)mi
->mergeableObj(ix
);
1547 if (LIKELY(uintptr_t(pre
) & 1)) {
1548 Stats::inc(Stats::UnitMerge_hoistable
);
1549 Class
* cls
= (Class
*)(uintptr_t(pre
) & ~1);
1550 FTRACE(3, " Merging cls {}\n", cls
->name()->data());
1551 auto const handle
= cls
->classHandle();
1552 auto const handle_persistent
= rds::isPersistentHandle(handle
);
1553 if (cls
->isPersistent()) {
1554 Stats::inc(Stats::UnitMerge_hoistable_persistent
);
1556 if (Stats::enabled() && handle_persistent
) {
1557 Stats::inc(Stats::UnitMerge_hoistable_persistent_cache
);
1559 if (Class
* parent
= cls
->parent()) {
1560 auto const parent_handle
= parent
->classHandle();
1561 auto const parent_handle_persistent
=
1562 rds::isPersistentHandle(parent_handle
);
1563 if (parent
->isPersistent()) {
1564 Stats::inc(Stats::UnitMerge_hoistable_persistent_parent
);
1566 if (Stats::enabled() && parent_handle_persistent
) {
1567 Stats::inc(Stats::UnitMerge_hoistable_persistent_parent_cache
);
1569 auto const parent_cls_present
=
1570 rds::isHandleInit(parent_handle
) &&
1571 rds::handleToRef
<LowPtr
<Class
>, rds::Mode::NonLocal
>(parent_handle
);
1572 if (UNLIKELY(!parent_cls_present
)) {
1573 define
.set(ix
- first
);
1577 if (handle_persistent
) {
1578 rds::handleToRef
<LowPtr
<Class
>, rds::Mode::Persistent
>(handle
) = cls
;
1580 assertx(rds::isNormalHandle(handle
));
1581 rds::handleToRef
<LowPtr
<Class
>, rds::Mode::Normal
>(handle
) = cls
;
1582 rds::initHandle(handle
);
1584 if (debugger
) phpDebuggerDefClassHook(cls
);
1586 if (UNLIKELY(!defClass(pre
, false))) define
.set(ix
- first
);
1589 // iterate over everything else and add to define set
1591 while (ix
< mi
->m_mergeablesSize
) {
1592 void* obj
= mi
->mergeableObj(ix
);
1593 auto k
= MergeKind(uintptr_t(obj
) & 7);
1594 if (k
== MergeKind::Done
) break;
1595 define
.set(ix
- first
);
1599 FTRACE(4, " {} top level entities left to define\n", define
.count());
1601 // iterate over the define set until we can make no progress
1602 // if there are still things left to be define, at that point just fatal.
1603 bool failIsFatal
= false;
1604 // We'll exit this while loop either by defining everything or fataling
1605 while (!define
.none()) {
1606 bool madeProgress
= false;
1607 auto i
= define
.find_first();
1609 // If we are about to fail, we need to give the error message of first
1610 // non hoistable in order maintain backwards compat
1611 while (i
!= define
.npos
&& i
< end
- first
) i
= define
.find_next(i
);
1612 if (i
== define
.npos
) i
= define
.find_first();
1614 for (; i
!= define
.npos
; i
= define
.find_next(i
)) {
1615 void* obj
= mi
->mergeableObj(i
+ first
);
1617 // Consider the above optimization
1618 if (i
< end
- first
&& UNLIKELY(uintptr_t(obj
) & 1)) {
1619 Class
* cls
= (Class
*)(uintptr_t(obj
) & ~1);
1620 if (defClass(cls
->preClass(), failIsFatal
)) {
1621 madeProgress
= true;
1627 auto k
= MergeKind(uintptr_t(obj
) & 7);
1629 case MergeKind::Class
: {
1630 Stats::inc(Stats::UnitMerge_mergeable
);
1631 Stats::inc(Stats::UnitMerge_mergeable_class
);
1632 if (defClass((PreClass
*)obj
, failIsFatal
)) {
1633 madeProgress
= true;
1639 case MergeKind::UniqueDefinedClass
: {
1640 Stats::inc(Stats::UnitMerge_mergeable
);
1641 Stats::inc(Stats::UnitMerge_mergeable_unique
);
1642 Class
* other
= nullptr;
1643 Class
* cls
= (Class
*)((char*)obj
- (int)k
);
1644 FTRACE(3, " Merging cls {}\n", cls
->name()->data());
1645 auto const handle
= cls
->classHandle();
1646 auto const handle_persistent
= rds::isPersistentHandle(handle
);
1647 if (cls
->isPersistent()) {
1648 Stats::inc(Stats::UnitMerge_mergeable_unique_persistent
);
1650 if (Stats::enabled() && handle_persistent
) {
1651 Stats::inc(Stats::UnitMerge_mergeable_unique_persistent_cache
);
1653 Class::Avail avail
= cls
->avail(other
, true);
1654 if (UNLIKELY(avail
== Class::Avail::Fail
)) {
1655 if (!failIsFatal
) continue;
1656 raise_error("unknown class %s", other
->name()->data());
1658 madeProgress
= true;
1660 assertx(avail
== Class::Avail::True
);
1661 if (handle_persistent
) {
1662 rds::handleToRef
<LowPtr
<Class
>,
1663 rds::Mode::Persistent
>(handle
) = cls
;
1665 assertx(rds::isNormalHandle(handle
));
1666 rds::handleToRef
<LowPtr
<Class
>,
1667 rds::Mode::Normal
>(handle
) = cls
;
1668 rds::initHandle(handle
);
1670 if (debugger
) phpDebuggerDefClassHook(cls
);
1674 case MergeKind::Define
: {
1675 Stats::inc(Stats::UnitMerge_mergeable
);
1676 Stats::inc(Stats::UnitMerge_mergeable_define
);
1677 auto const constantId
= static_cast<Id
>(intptr_t(obj
)) >> 3;
1679 madeProgress
= true;
1684 case MergeKind::TypeAlias
: {
1685 Stats::inc(Stats::UnitMerge_mergeable
);
1686 Stats::inc(Stats::UnitMerge_mergeable_typealias
);
1687 auto const aliasId
= static_cast<Id
>(intptr_t(obj
)) >> 3;
1688 auto const def
= defTypeAlias(aliasId
, failIsFatal
);
1689 if (def
== Unit::DefTypeAliasResult::Fail
) continue;
1690 if (def
== Unit::DefTypeAliasResult::Normal
) {
1691 auto& attrs
= m_typeAliases
[aliasId
].attrs
;
1692 if (attrs
& AttrPersistent
) {
1693 attrs
= static_cast<Attr
>(attrs
& ~AttrPersistent
);
1696 madeProgress
= true;
1701 case MergeKind::Record
: {
1702 Stats::inc(Stats::UnitMerge_mergeable
);
1703 Stats::inc(Stats::UnitMerge_mergeable_record
);
1704 auto const recordId
= static_cast<Id
>(intptr_t(obj
)) >> 3;
1705 auto const r
= lookupPreRecordId(recordId
);
1706 if (defRecordDesc(r
, failIsFatal
)) {
1707 madeProgress
= true;
1713 case MergeKind::Done
:
1718 if (!madeProgress
) failIsFatal
= true;
1721 if (UNLIKELY(m_mergeState
.load(std::memory_order_relaxed
) &
1722 MergeState::NeedsCompact
)) {
1723 SimpleLock
lock(unitInitLock
);
1724 if (!(m_mergeState
.load(std::memory_order_relaxed
) &
1725 MergeState::NeedsCompact
)) {
1729 * All the classes are known to be unique, and we just got
1730 * here, so all were successfully defined. We can now go
1731 * back and convert all MergeKind::Class entries to
1732 * MergeKind::UniqueDefinedClass, and all hoistable
1733 * classes to their Class*'s instead of PreClass*'s.
1735 * We can also remove any Persistent Class/Func*'s,
1736 * and any requires of modules that are (now) empty
1738 size_t delta
= compactMergeInfo(mi
, nullptr, m_typeAliases
,
1740 MergeInfo
* newMi
= mi
;
1742 newMi
= MergeInfo::alloc(mi
->m_mergeablesSize
- delta
);
1745 * In the case where mi == newMi, there's an apparent
1746 * race here. Although we have a lock, so we're the only
1747 * ones modifying this, there could be any number of
1748 * readers. But thats ok, because it doesnt matter
1749 * whether they see the old contents or the new.
1751 compactMergeInfo(mi
, newMi
, m_typeAliases
, m_constants
);
1753 this->m_mergeInfo
.store(newMi
, std::memory_order_release
);
1754 Treadmill::deferredFree(mi
);
1755 if (newMi
->m_mergeablesSize
== 0) {
1756 m_mergeState
.fetch_or(MergeState::Empty
,
1757 std::memory_order_relaxed
);
1760 m_mergeState
.fetch_and(~MergeState::NeedsCompact
,
1761 std::memory_order_relaxed
);
1765 ///////////////////////////////////////////////////////////////////////////////
1770 Array
getClassesWithAttrInfo(Attr attrs
, bool inverse
= false) {
1771 Array a
= Array::CreateVArray();
1772 NamedEntity::foreach_cached_class([&](Class
* c
) {
1773 if ((c
->attrs() & attrs
) ? !inverse
: inverse
) {
1774 if (c
->isBuiltin()) {
1775 a
.prepend(make_tv
<KindOfPersistentString
>(c
->name()));
1777 a
.append(make_tv
<KindOfPersistentString
>(c
->name()));
1784 template<bool system
>
1785 Array
getFunctions() {
1786 // Return an array of all defined functions. This method is used
1787 // to support get_defined_functions().
1788 Array a
= Array::CreateVArray();
1789 NamedEntity::foreach_cached_func([&](Func
* func
) {
1790 if ((system
^ func
->isBuiltin()) || func
->isGenerated()) return; //continue
1791 a
.append(HHVM_FN(strtolower
)(func
->nameStr()));
1796 ///////////////////////////////////////////////////////////////////////////////
1799 Array
Unit::getClassesInfo() {
1800 return getClassesWithAttrInfo(AttrInterface
| AttrTrait
,
1801 /* inverse = */ true);
1804 Array
Unit::getInterfacesInfo() {
1805 return getClassesWithAttrInfo(AttrInterface
);
1808 Array
Unit::getTraitsInfo() {
1809 return getClassesWithAttrInfo(AttrTrait
);
1812 Array
Unit::getUserFunctions() {
1813 return getFunctions
<false>();
1816 Array
Unit::getSystemFunctions() {
1817 return getFunctions
<true>();
1821 ///////////////////////////////////////////////////////////////////////////////
1824 void Unit::prettyPrint(std::ostream
& out
, PrintOpts opts
) const {
1825 auto startOffset
= opts
.startOffset
!= kInvalidOffset
1826 ? opts
.startOffset
: 0;
1827 auto stopOffset
= opts
.stopOffset
!= kInvalidOffset
1828 ? opts
.stopOffset
: m_bclen
;
1830 auto print
= [&](const Func
* func
, Offset startOffset
, Offset stopOffset
) {
1831 startOffset
= std::max(func
->base(), startOffset
);
1832 stopOffset
= std::min(func
->past(), stopOffset
);
1833 if (startOffset
>= stopOffset
) {
1837 if (opts
.showFuncs
&& startOffset
== func
->base()) {
1839 func
->prettyPrint(out
);
1842 const auto* it
= &m_bc
[startOffset
];
1843 int prevLineNum
= -1;
1844 while (it
< &m_bc
[stopOffset
]) {
1845 if (opts
.showLines
) {
1846 int lineNum
= getLineNumber(func
->offsetOf(it
));
1847 if (lineNum
!= prevLineNum
) {
1848 out
<< " // line " << lineNum
<< std::endl
;
1849 prevLineNum
= lineNum
;
1853 out
<< std::string(opts
.indentSize
, ' ')
1854 << std::setw(4) << (it
- m_bc
) << ": "
1855 << instrToString(it
, func
)
1861 std::map
<Offset
,const Func
*> funcMap
;
1862 for (auto& func
: funcs()) {
1863 print(func
, startOffset
, stopOffset
);
1865 for (auto it
= m_preClasses
.begin();
1866 it
!= m_preClasses
.end(); ++it
) {
1867 Func
* const* methods
= (*it
)->methods();
1868 size_t const numMethods
= (*it
)->numMethods();
1869 for (size_t i
= 0; i
< numMethods
; ++i
) {
1870 print(methods
[i
], startOffset
, stopOffset
);
1875 std::string
Unit::toString() const {
1876 std::ostringstream ss
;
1878 for (auto& pc
: m_preClasses
) {
1879 pc
->prettyPrint(ss
);
1881 for (auto& func
: funcs()) {
1882 func
->prettyPrint(ss
);
1884 for (auto& cns
: constants()) {
1885 cns
.prettyPrint(ss
);
1891 ///////////////////////////////////////////////////////////////////////////////
1894 std::string
mangleReifiedGenericsName(const ArrayData
* tsList
) {
1895 std::vector
<std::string
> l
;
1899 assertx(tvIsHAMSafeDArray(v
));
1901 TypeStructure::toString(ArrNR(v
.m_data
.parr
),
1902 TypeStructure::TSDisplayType::TSDisplayTypeInternal
).toCppString();
1903 str
.erase(remove_if(str
.begin(), str
.end(), isspace
), str
.end());
1904 l
.emplace_back(str
);
1907 return folly::sformat("<{}>", folly::join(",", l
));