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>
31 #include <folly/Format.h>
33 #include <tbb/concurrent_hash_map.h>
35 #include "hphp/util/alloc.h"
36 #include "hphp/util/assertions.h"
37 #include "hphp/util/compilation-flags.h"
38 #include "hphp/util/functional.h"
39 #include "hphp/util/lock.h"
40 #include "hphp/util/mutex.h"
41 #include "hphp/util/smalllocks.h"
43 #include "hphp/runtime/base/attr.h"
44 #include "hphp/runtime/base/autoload-handler.h"
45 #include "hphp/runtime/base/execution-context.h"
46 #include "hphp/runtime/base/packed-array.h"
47 #include "hphp/runtime/base/rds.h"
48 #include "hphp/runtime/base/runtime-error.h"
49 #include "hphp/runtime/base/runtime-option.h"
50 #include "hphp/runtime/base/stats.h"
51 #include "hphp/runtime/base/string-data.h"
52 #include "hphp/runtime/base/strings.h"
53 #include "hphp/runtime/base/tv-mutate.h"
54 #include "hphp/runtime/base/tv-variant.h"
55 #include "hphp/runtime/base/tv-refcount.h"
56 #include "hphp/runtime/base/type-array.h"
57 #include "hphp/runtime/base/type-string.h"
58 #include "hphp/runtime/base/type-variant.h"
59 #include "hphp/runtime/base/typed-value.h"
60 #include "hphp/runtime/base/unit-cache.h"
62 #include "hphp/runtime/vm/bytecode.h"
63 #include "hphp/runtime/vm/class.h"
64 #include "hphp/runtime/vm/debug/debug.h"
65 #include "hphp/runtime/vm/debugger-hook.h"
66 #include "hphp/runtime/vm/func.h"
67 #include "hphp/runtime/vm/hh-utils.h"
68 #include "hphp/runtime/vm/hhbc-codec.h"
69 #include "hphp/runtime/vm/hhbc.h"
70 #include "hphp/runtime/vm/instance-bits.h"
71 #include "hphp/runtime/vm/named-entity.h"
72 #include "hphp/runtime/vm/named-entity-defs.h"
73 #include "hphp/runtime/vm/preclass.h"
74 #include "hphp/runtime/vm/repo.h"
75 #include "hphp/runtime/vm/reverse-data-map.h"
76 #include "hphp/runtime/vm/treadmill.h"
77 #include "hphp/runtime/vm/type-alias.h"
78 #include "hphp/runtime/vm/unit-emitter.h"
79 #include "hphp/runtime/vm/unit-util.h"
80 #include "hphp/runtime/vm/vm-regs.h"
82 #include "hphp/runtime/server/source-root-info.h"
84 #include "hphp/runtime/ext/std/ext_std_closure.h"
85 #include "hphp/runtime/ext/string/ext_string.h"
87 #include "hphp/system/systemlib.h"
91 //////////////////////////////////////////////////////////////////////
95 //////////////////////////////////////////////////////////////////////
97 const StaticString
s_stdin("STDIN");
98 const StaticString
s_stdout("STDOUT");
99 const StaticString
s_stderr("STDERR");
101 //////////////////////////////////////////////////////////////////////
104 * Read typed data from an offset relative to a base address
107 T
& getDataRef(void* base
, unsigned offset
) {
108 return *reinterpret_cast<T
*>(static_cast<char*>(base
) + offset
);
111 //////////////////////////////////////////////////////////////////////
114 * We store 'detailed' line number information on a table on the side, because
115 * in production modes for HHVM it's generally not useful (which keeps Unit
116 * smaller in that case)---this stuff is only used for the debugger, where we
117 * can afford the lookup here. The normal Unit m_lineMap is capable of
118 * producing enough line number information for things needed in production
119 * modes (backtraces, warnings, etc).
122 using LineToOffsetRangeVecMap
= std::map
<int,OffsetRangeVec
>;
124 struct ExtendedLineInfo
{
125 SourceLocTable sourceLocTable
;
128 * Map from source lines to a collection of all the bytecode ranges the line
131 * The value type of the map is a list of offset ranges, so a single line
132 * with several sub-statements may correspond to the bytecodes of all of the
135 * May not be initialized. Lookups need to check if it's empty() and if so
136 * compute it from sourceLocTable.
138 LineToOffsetRangeVecMap lineToOffsetRange
;
141 using ExtendedLineInfoCache
= tbb::concurrent_hash_map
<
146 ExtendedLineInfoCache s_extendedLineInfo
;
148 using LineTableStash
= tbb::concurrent_hash_map
<
153 LineTableStash s_lineTables
;
155 struct LineCacheEntry
{
156 LineCacheEntry(const Unit
* unit
, LineTable
&& table
)
158 , table
{std::move(table
)}
163 std::array
<std::atomic
<LineCacheEntry
*>, 512> s_lineCache
;
165 //////////////////////////////////////////////////////////////////////
169 ///////////////////////////////////////////////////////////////////////////////
172 Unit::MergeInfo
* Unit::MergeInfo::alloc(size_t size
) {
173 MergeInfo
* mi
= (MergeInfo
*)malloc(
174 sizeof(MergeInfo
) + size
* sizeof(void*));
175 mi
->m_firstHoistableFunc
= 0;
176 mi
->m_firstHoistablePreClass
= 0;
177 mi
->m_firstMergeablePreClass
= 0;
178 mi
->m_mergeablesSize
= size
;
183 ///////////////////////////////////////////////////////////////////////////////
184 // Construction and destruction.
188 , m_interpretOnly(false)
191 , m_mainReturn(make_tv
<KindOfUninit
>())
195 if (RuntimeOption::EvalEnableReverseDataMap
&&
196 m_mergeState
.load(std::memory_order_relaxed
) != MergeState::Unmerged
) {
197 // Units are registered to data_map in Unit::initialMerge().
198 data_map::deregister(this);
201 s_extendedLineInfo
.erase(this);
202 s_lineTables
.erase(this);
204 auto const hash
= pointer_hash
<Unit
>{}(this) % s_lineCache
.size();
205 auto& entry
= s_lineCache
[hash
];
206 if (auto lce
= entry
.load(std::memory_order_acquire
)) {
207 if (lce
->unit
== this &&
208 entry
.compare_exchange_strong(lce
, nullptr,
209 std::memory_order_release
)) {
210 Treadmill::enqueue([lce
] { delete lce
; });
214 if (!RuntimeOption::RepoAuthoritative
) {
216 // poison released bytecode
217 memset(const_cast<unsigned char*>(m_bc
), 0xff, m_bclen
);
219 free(const_cast<unsigned char*>(m_bc
));
220 g_hhbc_size
->addValue(-int64_t(m_bclen
));
223 auto const mi
= mergeInfo();
225 for (auto const func
: mi
->mutableFuncs()) Func::destroy(func
);
228 // ExecutionContext and the TC may retain references to Class'es, so
229 // it is possible for Class'es to outlive their Unit.
230 for (auto const& pcls
: m_preClasses
) {
231 Class
* cls
= pcls
->namedEntity()->clsList();
234 cls
= cls
->m_nextClass
;
235 if (cur
->preClass() == pcls
.get()) {
243 if (m_pseudoMainCache
) {
244 for (auto& kv
: *m_pseudoMainCache
) {
245 Func::destroy(kv
.second
);
247 delete m_pseudoMainCache
;
251 void* Unit::operator new(size_t sz
) {
252 return low_malloc_data(sz
);
255 void Unit::operator delete(void* p
, size_t /*sz*/) {
260 ///////////////////////////////////////////////////////////////////////////////
263 static SourceLocTable
loadSourceLocTable(const Unit
* unit
) {
264 auto ret
= SourceLocTable
{};
265 if (unit
->repoID() == RepoIdInvalid
) return ret
;
267 Lock
lock(g_classesMutex
);
268 auto& urp
= Repo::get().urp();
269 urp
.getSourceLocTab
[unit
->repoID()].get(unit
->sn(), ret
);
274 * Return the Unit's SourceLocTable, extracting it from the repo if
277 const SourceLocTable
& getSourceLocTable(const Unit
* unit
) {
279 ExtendedLineInfoCache::const_accessor acc
;
280 if (s_extendedLineInfo
.find(acc
, unit
)) {
281 return acc
->second
.sourceLocTable
;
285 // Try to load it while we're not holding the lock.
286 auto newTable
= loadSourceLocTable(unit
);
287 ExtendedLineInfoCache::accessor acc
;
288 if (s_extendedLineInfo
.insert(acc
, unit
)) {
289 acc
->second
.sourceLocTable
= std::move(newTable
);
291 return acc
->second
.sourceLocTable
;
295 * Generate line->vector<OffsetRange> reverse map from SourceLocTable.
298 * We first generate the OffsetRange for each SourceLoc,
299 * then sort the pair<SourceLoc, OffsetRange> in most nested to outward order
300 * so that we can add vector<OffsetRange> for nested lines first.
301 * After merging continuous duplicate line ranges into one we build the final
302 * map by adding vector<OffsetRange> for each line in the LineRange only if
303 * it hasn't got any vector<OffsetRange> from inner LineRange yet.
304 * By doing this we ensure the outer LineRange's vector<OffsetRange> will not be
305 * added for inner lines.
307 static void generateLineToOffsetRangesMap(
309 LineToOffsetRangeVecMap
& map
311 // First generate an OffsetRange for each SourceLoc.
312 auto const& srcLocTable
= getSourceLocTable(unit
);
315 LineRange(int start
, int end
)
316 : line0(start
), line1(end
)
321 bool operator!=(const LineRange
& other
) const {
322 return this->line0
!= other
.line0
|| this->line1
!= other
.line1
;
326 using LineRangeOffsetRangePair
= std::pair
<LineRange
, OffsetRange
>;
327 std::vector
<LineRangeOffsetRangePair
> lineRangesTable
;
329 for (const auto& sourceLoc
: srcLocTable
) {
330 Offset pastOff
= sourceLoc
.pastOffset();
331 OffsetRange
offsetRange(baseOff
, pastOff
);
332 LineRange
lineRange(sourceLoc
.val().line0
, sourceLoc
.val().line1
);
333 lineRangesTable
.emplace_back(lineRange
, offsetRange
);
337 // Sort the line ranges in most nested to outward order:
338 // First sort them in ascending order by line range end;
339 // if range end ties, sort in descending order by line range start.
341 lineRangesTable
.begin(),
342 lineRangesTable
.end(),
343 [](const LineRangeOffsetRangePair
& a
, const LineRangeOffsetRangePair
& b
) {
344 return a
.first
.line1
== b
.first
.line1
?
345 a
.first
.line0
> b
.first
.line0
:
346 a
.first
.line1
< b
.first
.line1
;
350 // Merge continuous duplicate line ranges into one.
351 using LineRangeToOffsetRangesTable
=
352 std::vector
<std::pair
<LineRange
, std::vector
<OffsetRange
>>>;
353 LineRangeToOffsetRangesTable lineRangeToOffsetRangesTable
;
354 for (auto i
= 0; i
< lineRangesTable
.size(); ++i
) {
355 if (i
== 0 || lineRangesTable
[i
].first
!= lineRangesTable
[i
-1].first
) {
356 // New line range starts.
357 std::vector
<OffsetRange
> offsetRanges
;
358 offsetRanges
.emplace_back(lineRangesTable
[i
].second
);
359 const auto& lineRange
= lineRangesTable
[i
].first
;
360 lineRangeToOffsetRangesTable
.emplace_back(lineRange
, offsetRanges
);
362 // Duplicate LineRange.
363 assertx(lineRangeToOffsetRangesTable
.size() > 0);
364 auto& offsetRanges
= lineRangeToOffsetRangesTable
.back().second
;
365 offsetRanges
.emplace_back(lineRangesTable
[i
].second
);
369 // Generate the final line to offset ranges map.
370 for (auto& entry
: lineRangeToOffsetRangesTable
) {
371 // Sort the offset ranges of each line range.
373 entry
.second
.begin(),
375 [](const OffsetRange
& a
, const OffsetRange
& b
) {
376 return a
.base
== b
.base
? a
.past
< b
.past
: a
.base
< b
.base
;
380 const auto& offsetRanges
= entry
.second
;
381 auto line0
= entry
.first
.line0
;
382 auto line1
= entry
.first
.line1
;
383 for (auto line
= line0
; line
<= line1
; ++line
) {
384 // Only add if not added by inner LineRange yet.
385 if (map
.find(line
) == map
.end()) {
386 map
[line
] = offsetRanges
;
393 * Return a copy of the Unit's line to OffsetRangeVec table.
395 static LineToOffsetRangeVecMap
getLineToOffsetRangeVecMap(const Unit
* unit
) {
397 ExtendedLineInfoCache::const_accessor acc
;
398 if (s_extendedLineInfo
.find(acc
, unit
)) {
399 if (!acc
->second
.lineToOffsetRange
.empty()) {
400 return acc
->second
.lineToOffsetRange
;
405 LineToOffsetRangeVecMap map
;
406 generateLineToOffsetRangesMap(unit
, map
);
408 ExtendedLineInfoCache::accessor acc
;
409 if (!s_extendedLineInfo
.find(acc
, unit
)) {
410 always_assert_flog(0, "ExtendedLineInfoCache was not found when it should "
413 if (acc
->second
.lineToOffsetRange
.empty()) {
414 acc
->second
.lineToOffsetRange
= std::move(map
);
416 return acc
->second
.lineToOffsetRange
;
419 static const LineTable
& loadLineTable(const Unit
* unit
) {
420 if (unit
->repoID() == RepoIdInvalid
) {
421 LineTableStash::accessor acc
;
422 if (s_lineTables
.find(acc
, unit
)) {
425 static LineTable empty
;
429 auto const hash
= pointer_hash
<Unit
>{}(unit
) % s_lineCache
.size();
430 auto& entry
= s_lineCache
[hash
];
431 if (auto const p
= entry
.load(std::memory_order_acquire
)) {
432 if (p
->unit
== unit
) return p
->table
;
435 // We already hold a lock on the unit in Unit::getLineNumber below,
436 // so nobody else is going to be reading the line table while we are
437 // (this is only an efficiency concern).
438 auto& urp
= Repo::get().urp();
439 auto table
= LineTable
{};
440 urp
.getUnitLineTable(unit
->repoID(), unit
->sn(), table
);
441 auto const p
= new LineCacheEntry(unit
, std::move(table
));
442 if (auto const old
= entry
.exchange(p
, std::memory_order_release
)) {
443 Treadmill::enqueue([old
] { delete old
; });
448 static LineInfo
getLineInfo(const LineTable
& table
, Offset pc
) {
450 std::upper_bound(begin(table
), end(table
), LineEntry
{ pc
, -1 });
452 auto const e
= end(table
);
454 auto const line
= it
->val();
456 auto const pastOff
= it
->pastOffset();
457 auto const baseOff
= it
== begin(table
) ?
458 pc
: std::prev(it
)->pastOffset();
459 assertx(baseOff
<= pc
&& pc
< pastOff
);
460 return { { baseOff
, pastOff
}, line
};
463 return LineInfo
{ { pc
, pc
+ 1 }, -1 };
466 int getLineNumber(const LineTable
& table
, Offset pc
) {
467 auto const key
= LineEntry(pc
, -1);
468 auto it
= std::upper_bound(begin(table
), end(table
), key
);
469 if (it
!= end(table
)) {
470 assertx(pc
< it
->pastOffset());
476 int Unit::getLineNumber(Offset pc
) const {
477 if (UNLIKELY(m_repoId
== RepoIdInvalid
)) {
478 auto const lineTable
= [&] () -> const LineTable
* {
479 LineTableStash::accessor acc
;
480 if (s_lineTables
.find(acc
, this)) {
485 if (lineTable
) return HPHP::getLineNumber(*lineTable
, pc
);
488 auto findLine
= [&] {
489 // lineMap is an atomically acquired bitwise copy of m_lineMap,
490 // with no destructor
491 auto lineMap(m_lineMap
.get());
492 if (lineMap
->empty()) return INT_MIN
;
493 auto const it
= std::upper_bound(
494 lineMap
->begin(), lineMap
->end(),
495 *lineMap
->begin(), // Will be the first (ignored) param to our predicate
496 [&] (const LineInfo
&, const LineInfo
& elm
) {
497 return pc
< elm
.first
.past
;
500 if (it
!= lineMap
->end() && it
->first
.base
<= pc
) return it
->second
;
504 auto line
= findLine();
505 if (line
!= INT_MIN
) return line
;
507 m_lineMap
.lock_for_update();
510 if (line
!= INT_MIN
) {
515 auto const info
= HPHP::getLineInfo(loadLineTable(this), pc
);
516 auto copy
= m_lineMap
.copy();
517 auto const it
= std::upper_bound(
518 copy
.begin(), copy
.end(),
520 [&] (const LineInfo
& a
, const LineInfo
& b
) {
521 return a
.first
.base
< b
.first
.past
;
524 assertx(it
== copy
.end() || (it
->first
.past
> pc
&& it
->first
.base
> pc
));
525 copy
.insert(it
, info
);
526 auto old
= m_lineMap
.update_and_unlock(std::move(copy
));
527 Treadmill::enqueue([old
= std::move(old
)] () mutable { old
.clear(); });
535 bool getSourceLoc(const SourceLocTable
& table
, Offset pc
, SourceLoc
& sLoc
) {
536 SourceLocEntry
key(pc
, sLoc
);
537 auto it
= std::upper_bound(table
.begin(), table
.end(), key
);
538 if (it
!= table
.end()) {
539 assertx(pc
< it
->pastOffset());
546 bool Unit::getSourceLoc(Offset pc
, SourceLoc
& sLoc
) const {
547 auto const& sourceLocTable
= getSourceLocTable(this);
548 return HPHP::getSourceLoc(sourceLocTable
, pc
, sLoc
);
551 bool Unit::getOffsetRange(Offset pc
, OffsetRange
& range
) const {
552 OffsetRangeVec offsets
;
553 auto line
= getLineNumber(pc
);
554 getOffsetRanges(line
, offsets
);
556 for (auto offset
: offsets
) {
557 if (pc
>= offset
.base
&& pc
< offset
.past
) {
565 bool Unit::getOffsetRanges(int line
, OffsetRangeVec
& offsets
) const {
566 assertx(offsets
.size() == 0);
567 auto map
= getLineToOffsetRangeVecMap(this);
568 auto it
= map
.find(line
);
569 if (it
== map
.end()) return false;
570 offsets
= it
->second
;
574 int Unit::getNearestLineWithCode(int line
) const {
575 auto map
= getLineToOffsetRangeVecMap(this);
576 auto it
= map
.lower_bound(line
);
577 return it
== map
.end() ? -1 : it
->first
;
580 const Func
* Unit::getFunc(Offset pc
) const {
581 auto& table
= getExtended()->m_funcTable
;
582 auto it
= std::upper_bound(table
.begin(), table
.end(), nullptr,
583 [&] (const Func
* a
, const Func
* b
) {
584 assertx(a
== nullptr);
585 return pc
< b
->past();
587 if (it
!= table
.end()) {
588 assertx(pc
< (*it
)->past());
594 void stashLineTable(const Unit
* unit
, LineTable table
) {
595 LineTableStash::accessor acc
;
596 if (s_lineTables
.insert(acc
, unit
)) {
597 acc
->second
= std::move(table
);
601 void stashExtendedLineTable(const Unit
* unit
, SourceLocTable table
) {
602 ExtendedLineInfoCache::accessor acc
;
603 if (s_extendedLineInfo
.insert(acc
, unit
)) {
604 acc
->second
.sourceLocTable
= std::move(table
);
608 ///////////////////////////////////////////////////////////////////////////////
609 // Funcs and PreClasses.
611 Func
* Unit::getMain(Class
* cls
/* = nullptr */) const {
612 auto const mi
= mergeInfo();
613 if (!cls
) return *mi
->funcBegin();
614 Lock
lock(g_classesMutex
);
615 if (!m_pseudoMainCache
) {
616 m_pseudoMainCache
= new PseudoMainCacheMap
;
618 auto it
= m_pseudoMainCache
->find(cls
);
619 if (it
!= m_pseudoMainCache
->end()) {
622 Func
* f
= (*mi
->funcBegin())->clone(cls
);
625 (*m_pseudoMainCache
)[cls
] = f
;
629 void Unit::renameFunc(const StringData
* oldName
, const StringData
* newName
) {
630 // We do a linear scan over all the functions in the unit searching for the
631 // func with a given name; in practice this is okay because the units created
632 // by create_function() will always have the function being renamed at the
634 assertx(oldName
&& oldName
->isStatic());
635 assertx(newName
&& newName
->isStatic());
637 for (auto& func
: mergeInfo()->hoistableFuncs()) {
638 auto const name
= func
->name();
640 if (name
->same(oldName
)) {
641 func
->rename(newName
);
648 ///////////////////////////////////////////////////////////////////////////////
651 void Unit::defFunc(Func
* func
, bool debugger
) {
652 assertx(!func
->isMethod());
653 auto const handle
= func
->funcHandle();
654 auto& funcAddr
= rds::handleToRef
<LowPtr
<Func
>>(handle
);
656 if (rds::isPersistentHandle(handle
)) {
657 auto const oldFunc
= funcAddr
.get();
658 if (oldFunc
== func
) return;
659 if (UNLIKELY(oldFunc
!= nullptr)) {
660 assertx(oldFunc
->isBuiltin() && !func
->isBuiltin());
661 raise_error(Strings::REDECLARE_BUILTIN
, func
->name()->data());
664 assertx(rds::isNormalHandle(handle
));
665 if (!rds::isHandleInit(handle
, rds::NormalTag
{})) {
666 rds::initHandle(handle
);
668 if (funcAddr
.get() == func
) return;
669 raise_error(Strings::FUNCTION_ALREADY_DEFINED
, func
->name()->data());
674 if (func
->isUnique()) func
->getNamedEntity()->setUniqueFunc(func
);
676 if (UNLIKELY(debugger
)) phpDebuggerDefFuncHook(func
);
679 Func
* Unit::lookupFunc(const NamedEntity
* ne
) {
680 return ne
->getCachedFunc();
683 Func
* Unit::lookupFunc(const StringData
* name
) {
684 const NamedEntity
* ne
= NamedEntity::get(name
);
685 return ne
->getCachedFunc();
688 Func
* Unit::lookupBuiltin(const StringData
* name
) {
689 // Builtins are either persistent (the normal case), or defined at the
690 // beginning of every request (if JitEnableRenameFunction or interception is
691 // enabled). In either case, they're unique, so they should be present in the
693 auto const ne
= NamedEntity::get(name
);
694 auto const f
= ne
->uniqueFunc();
695 return (f
&& f
->isBuiltin()) ? f
: nullptr;
698 Func
* Unit::loadFunc(const NamedEntity
* ne
, const StringData
* name
) {
699 Func
* func
= ne
->getCachedFunc();
700 if (LIKELY(func
!= nullptr)) return func
;
701 if (AutoloadHandler::s_instance
->autoloadFunc(
702 const_cast<StringData
*>(name
))) {
703 func
= ne
->getCachedFunc();
708 Func
* Unit::loadFunc(const StringData
* name
) {
710 auto ne
= NamedEntity::get(name
, true, &normStr
);
712 name
= normStr
.get();
714 return loadFunc(ne
, name
);
717 void Unit::bindFunc(Func
*func
) {
718 assertx(!func
->isMethod());
719 auto const ne
= func
->getNamedEntity();
720 ne
->m_cachedFunc
.bind(
722 auto const isPersistent
=
723 (RuntimeOption::RepoAuthoritative
|| !SystemLib::s_inited
) &&
724 (func
->attrs() & AttrPersistent
);
725 auto const link
= rds::alloc
<LowPtr
<const Func
>>(
726 isPersistent
? rds::Mode::Persistent
: rds::Mode::Normal
);
728 if (func
->isUnique()) ne
->setUniqueFunc(func
);
729 if (RuntimeOption::EvalPerfDataMap
) {
734 func
->name()->toCppString()
737 return link
.handle();
740 func
->setFuncHandle(ne
->m_cachedFunc
);
743 ///////////////////////////////////////////////////////////////////////////////
745 struct FrameRestore
{
746 explicit FrameRestore(const PreClass
* preClass
) :
747 FrameRestore(preClass
->unit(), preClass
->getOffset()) {}
748 explicit FrameRestore(const Unit
* unit
, Op op
, Id id
) :
749 FrameRestore(unit
, (static_cast<size_t>(op
) << 32) | id
) {}
750 explicit NEVER_INLINE
FrameRestore(const Unit
* unit
, size_t offsetOrOp
) {
753 if (vmsp() && (!fp
|| fp
->m_func
->unit() != unit
)) {
759 we can be called from Unit::merge, which hasnt yet setup
760 the frame (because often it doesnt need to).
761 Set up a fake frame here, in case of errors.
762 But note that mergeUnit is called for systemlib etc before the
763 stack has been setup. So dont do anything if m_stack.top()
766 ActRec
&tmp
= *vmStack().allocA();
769 tmp
.m_func
= unit
->getMain(nullptr);
772 : fp
->m_func
->unit()->offsetOf(m_pc
) - fp
->m_func
->base();
777 auto const offset
= [&] {
778 if (offsetOrOp
< kInvalidOffset
) return static_cast<Offset
>(offsetOrOp
);
779 auto const op
= Op(offsetOrOp
>> 32);
780 auto const id
= Id(offsetOrOp
& 0xffffffff);
781 auto pc
= unit
->at(tmp
.m_func
->base());
782 auto const past
= unit
->at(tmp
.m_func
->past());
784 if (peek_op(pc
) == op
) {
787 if (decode_iva(tpc
) == id
) return unit
->offsetOf(pc
);
791 return tmp
.m_func
->base();
793 vmpc() = unit
->at(offset
);
794 pushFrameSlots(tmp
.m_func
);
814 ///////////////////////////////////////////////////////////////////////////////
818 void setupClass(Class
* newClass
, NamedEntity
* nameList
) {
819 bool const isPersistent
=
820 (!SystemLib::s_inited
|| RuntimeOption::RepoAuthoritative
) &&
821 newClass
->verifyPersistent();
822 nameList
->m_cachedClass
.bind(
823 isPersistent
? rds::Mode::Persistent
: rds::Mode::Normal
);
825 newClass
->setClassHandle(nameList
->m_cachedClass
);
826 newClass
->incAtomicCount();
828 InstanceBits::ifInitElse(
829 [&] { newClass
->setInstanceBits();
830 nameList
->pushClass(newClass
); },
831 [&] { nameList
->pushClass(newClass
); }
834 if (RuntimeOption::EvalEnableReverseDataMap
) {
835 // The corresponding deregister is in NamedEntity::removeClass().
836 data_map::register_start(newClass
);
841 Class
* Unit::defClass(const PreClass
* preClass
,
842 bool failIsFatal
/* = true */) {
843 NamedEntity
* const nameList
= preClass
->namedEntity();
844 Class
* top
= nameList
->clsList();
847 * Check if there is already a name defined in this request for this
850 * Raise a fatal unless the existing class definition is identical to the
851 * one this invocation would create.
853 if (auto current
= nameList
->getCachedTypeAlias()) {
854 FrameRestore
fr(preClass
);
855 raise_error("Cannot declare class with the same name (%s) as an "
856 "existing type", current
->name
->data());
860 // If there was already a class declared with DefClass, check if it's
862 if (Class
* cls
= nameList
->getCachedClass()) {
863 if (cls
->preClass() != preClass
) {
865 FrameRestore
fr(preClass
);
866 raise_error("Class already declared: %s", preClass
->name()->data());
873 // Get a compatible Class, and add it to the list of defined classes.
874 Class
* parent
= nullptr;
876 // Search for a compatible extant class. Searching from most to least
877 // recently created may have better locality than alternative search orders.
878 // In addition, its the only simple way to make this work lock free...
879 for (Class
* class_
= top
; class_
!= nullptr; ) {
881 class_
= class_
->m_nextClass
;
882 if (cur
->preClass() != preClass
) continue;
883 Class::Avail avail
= cur
->avail(parent
, failIsFatal
/*tryAutoload*/);
884 if (LIKELY(avail
== Class::Avail::True
)) {
886 DEBUGGER_ATTACHED_ONLY(phpDebuggerDefClassHook(cur
));
889 if (avail
== Class::Avail::Fail
) {
891 FrameRestore
fr(preClass
);
892 raise_error("unknown class %s", parent
->name()->data());
896 assertx(avail
== Class::Avail::False
);
899 // Create a new class.
900 if (!parent
&& preClass
->parent()->size() != 0) {
901 parent
= Unit::getClass(preClass
->parent(), failIsFatal
);
902 if (parent
== nullptr) {
904 FrameRestore
fr(preClass
);
905 raise_error("unknown class %s", preClass
->parent()->data());
913 FrameRestore
fr(preClass
);
914 newClass
= Class::newClass(const_cast<PreClass
*>(preClass
), parent
);
916 Lock
l(g_classesMutex
);
918 if (UNLIKELY(top
!= nameList
->clsList())) {
919 top
= nameList
->clsList();
923 setupClass(newClass
.get(), nameList
);
926 * call setCached after adding to the class list, otherwise the
927 * target-cache short circuit at the top could return a class
928 * which is not yet on the clsList().
930 newClass
.get()->setCached();
931 DEBUGGER_ATTACHED_ONLY(phpDebuggerDefClassHook(newClass
.get()));
932 return newClass
.get();
936 Class
* Unit::defClosure(const PreClass
* preClass
) {
937 auto const nameList
= preClass
->namedEntity();
939 if (nameList
->clsList()) return nameList
->clsList();
941 auto const parent
= c_Closure::classof();
943 assertx(preClass
->parent() == parent
->name());
944 // Create a new class.
947 Class::newClass(const_cast<PreClass
*>(preClass
), parent
)
950 Lock
l(g_classesMutex
);
952 if (UNLIKELY(nameList
->clsList() != nullptr)) return nameList
->clsList();
954 setupClass(newClass
.get(), nameList
);
956 if (classHasPersistentRDS(newClass
.get())) newClass
.get()->setCached();
957 return newClass
.get();
961 bool isPHP7ReservedType(const StringData
* alias
) {
963 !strcmp("int", alias
->data()) ||
964 !strcmp("bool", alias
->data()) ||
965 !strcmp("float", alias
->data()) ||
966 !strcmp("string", alias
->data());
970 bool Unit::aliasClass(const StringData
* original
, const StringData
* alias
,
972 if (RuntimeOption::PHP7_ScalarTypes
&& isPHP7ReservedType(alias
)) {
973 raise_error("Fatal error: Cannot use '%s' as class name as it is reserved",
976 auto const origClass
=
977 autoload
? Unit::loadClass(original
)
978 : Unit::lookupClass(original
);
980 raise_warning("Class %s not found", original
->data());
983 if (origClass
->isBuiltin()) {
984 raise_warning("First argument of class_alias() must be "
985 "the name of a user defined class");
989 auto const aliasNe
= NamedEntity::get(alias
);
990 aliasNe
->m_cachedClass
.bind();
992 auto const aliasClass
= aliasNe
->getCachedClass();
994 raise_warning("Cannot redeclare class %s", alias
->data());
997 aliasNe
->setCachedClass(origClass
);
1001 Class
* Unit::loadClass(const NamedEntity
* ne
,
1002 const StringData
* name
) {
1004 if (LIKELY((cls
= ne
->getCachedClass()) != nullptr)) {
1007 return loadMissingClass(ne
, name
);
1010 Class
* Unit::loadMissingClass(const NamedEntity
* ne
,
1011 const StringData
* name
) {
1013 AutoloadHandler::s_instance
->autoloadClass(
1014 StrNR(const_cast<StringData
*>(name
)));
1015 return Unit::lookupClass(ne
);
1018 Class
* Unit::getClass(const NamedEntity
* ne
,
1019 const StringData
*name
, bool tryAutoload
) {
1020 Class
*cls
= lookupClass(ne
);
1021 if (UNLIKELY(!cls
)) {
1023 return loadMissingClass(ne
, name
);
1029 bool Unit::classExists(const StringData
* name
, bool autoload
, ClassKind kind
) {
1030 Class
* cls
= Unit::getClass(name
, autoload
);
1032 (cls
->attrs() & (AttrInterface
| AttrTrait
)) == classKindAsAttr(kind
);
1036 ///////////////////////////////////////////////////////////////////////////////
1039 const Cell
* Unit::lookupCns(const StringData
* cnsName
) {
1040 auto const handle
= lookupCnsHandle(cnsName
);
1042 if (LIKELY(rds::isHandleBound(handle
) &&
1043 rds::isHandleInit(handle
))) {
1044 auto const& tv
= rds::handleToRef
<TypedValue
>(handle
);
1046 if (LIKELY(tv
.m_type
!= KindOfUninit
)) {
1047 assertx(cellIsPlausible(tv
));
1051 assertx(tv
.m_data
.pref
!= nullptr);
1052 auto const callback
=
1053 reinterpret_cast<Native::ConstantCallback
>(tv
.m_data
.pref
);
1054 const Cell
* tvRet
= callback().asTypedValue();
1055 assertx(cellIsPlausible(*tvRet
));
1056 if (LIKELY(tvRet
->m_type
!= KindOfUninit
)) {
1061 if (UNLIKELY(rds::s_constants().get() != nullptr)) {
1062 return rds::s_constants()->rval(cnsName
).tv_ptr();
1067 const Cell
* Unit::lookupPersistentCns(const StringData
* cnsName
) {
1068 auto const handle
= lookupCnsHandle(cnsName
);
1069 if (!rds::isHandleBound(handle
) || !rds::isPersistentHandle(handle
)) {
1072 auto const ret
= &rds::handleToRef
<TypedValue
>(handle
);
1073 assertx(cellIsPlausible(*ret
));
1077 const TypedValue
* Unit::loadCns(const StringData
* cnsName
) {
1078 auto const tv
= lookupCns(cnsName
);
1079 if (LIKELY(tv
!= nullptr)) return tv
;
1081 if (needsNSNormalization(cnsName
)) {
1082 return loadCns(normalizeNS(cnsName
));
1085 if (!AutoloadHandler::s_instance
->autoloadConstant(
1086 const_cast<StringData
*>(cnsName
))) {
1089 return lookupCns(cnsName
);
1092 static bool defCnsHelper(rds::Handle ch
,
1093 const TypedValue
*value
,
1094 const StringData
*cnsName
) {
1095 TypedValue
* cns
= &rds::handleToRef
<TypedValue
>(ch
);
1097 if (!rds::isHandleInit(ch
)) {
1098 cns
->m_type
= KindOfUninit
;
1099 cns
->m_data
.pref
= nullptr;
1102 if (UNLIKELY(cns
->m_type
!= KindOfUninit
||
1103 cns
->m_data
.pref
!= nullptr)) {
1104 raise_notice(Strings::CONSTANT_ALREADY_DEFINED
, cnsName
->data());
1108 if (UNLIKELY(!tvAsCVarRef(value
).isAllowedAsConstantValue())) {
1109 raise_warning(Strings::CONSTANTS_MUST_BE_SCALAR
);
1113 assertx(rds::isNormalHandle(ch
));
1114 cellDup(*value
, *cns
);
1115 rds::initHandle(ch
);
1119 bool Unit::defCns(const StringData
* cnsName
, const TypedValue
* value
) {
1120 auto const handle
= makeCnsHandle(cnsName
);
1122 if (UNLIKELY(!rds::isHandleBound(handle
))) {
1123 if (UNLIKELY(!rds::s_constants().get())) {
1125 * This only happens when we call define on a non
1126 * static string. Not worth presizing or otherwise
1129 rds::s_constants() =
1130 Array::attach(PackedArray::MakeReserve(PackedArray::SmallSize
));
1132 auto const existed
= !!rds::s_constants()->rval(cnsName
);
1134 rds::s_constants().set(StrNR(cnsName
),
1135 tvAsCVarRef(value
), true /* isKey */);
1138 raise_notice(Strings::CONSTANT_ALREADY_DEFINED
, cnsName
->data());
1141 return defCnsHelper(handle
, value
, cnsName
);
1144 bool Unit::defNativeConstantCallback(const StringData
* cnsName
,
1146 static const bool kServer
= RuntimeOption::ServerExecutionMode();
1147 // Zend doesn't define the STD* streams in server mode so we don't either
1148 if (UNLIKELY(kServer
&&
1149 (s_stdin
.equal(cnsName
) ||
1150 s_stdout
.equal(cnsName
) ||
1151 s_stderr
.equal(cnsName
)))) {
1154 bindPersistentCns(cnsName
, value
);
1158 ///////////////////////////////////////////////////////////////////////////////
1163 TypeAliasReq
typeAliasFromClass(Unit
* unit
, const TypeAlias
* thisType
,
1168 req
.name
= thisType
->name
;
1169 req
.nullable
= thisType
->nullable
;
1170 if (isEnum(klass
)) {
1171 // If the class is an enum, pull out the actual base type.
1172 if (auto const enumType
= klass
->enumBaseTy()) {
1173 req
.type
= dataTypeToAnnotType(*enumType
);
1175 req
.type
= AnnotType::Mixed
;
1178 req
.type
= AnnotType::Object
;
1181 req
.userAttrs
= thisType
->userAttrs
;
1182 assertx(thisType
->typeStructure
.isDictOrDArray());
1183 req
.typeStructure
= thisType
->typeStructure
;
1187 TypeAliasReq
resolveTypeAlias(Unit
* unit
, const TypeAlias
* thisType
) {
1189 * If this type alias is a KindOfObject and the name on the right
1190 * hand side was another type alias, we will bind the name to the
1191 * other side for this request (i.e. resolve that type alias now).
1193 * We need to inspect the right hand side and figure out what it was
1196 * If the right hand side was a class, we need to autoload and
1197 * ensure it exists at this point.
1199 if (thisType
->type
!= AnnotType::Object
) {
1200 return TypeAliasReq::From(unit
, *thisType
);
1204 * If the right hand side is already defined, don't invoke the
1205 * autoloader at all, this means we have to check for both a type
1206 * alias and a class before attempting to load them via the
1209 * While normal autoloaders are fine, the "failure" entry in the
1210 * map passed to `HH\set_autoload_paths` is called for all failed
1211 * lookups. The failure function can do anything, including something
1212 * like like raising an error or rebuilding the map. We don't want to
1213 * send speculative (or worse, repeat) requests to the autoloader, so
1214 * do our due diligence here.
1217 const StringData
* typeName
= thisType
->value
;
1218 auto targetNE
= NamedEntity::get(typeName
);
1220 if (auto klass
= Unit::lookupClass(targetNE
)) {
1221 return typeAliasFromClass(unit
, thisType
, klass
);
1224 if (auto targetTd
= targetNE
->getCachedTypeAlias()) {
1225 return TypeAliasReq::From(unit
, *targetTd
, *thisType
);
1228 if (AutoloadHandler::s_instance
->autoloadClassOrType(
1229 StrNR(const_cast<StringData
*>(typeName
))
1231 if (auto klass
= Unit::lookupClass(targetNE
)) {
1232 return typeAliasFromClass(unit
, thisType
, klass
);
1234 if (auto targetTd
= targetNE
->getCachedTypeAlias()) {
1235 return TypeAliasReq::From(unit
, *targetTd
, *thisType
);
1239 return TypeAliasReq::Invalid(unit
);
1242 ///////////////////////////////////////////////////////////////////////////////
1245 const TypeAliasReq
* Unit::loadTypeAlias(const StringData
* name
,
1247 auto ne
= NamedEntity::get(name
);
1248 auto target
= ne
->getCachedTypeAlias();
1250 if (AutoloadHandler::s_instance
->autoloadClassOrType(
1251 StrNR(const_cast<StringData
*>(name
))
1253 target
= ne
->getCachedTypeAlias();
1259 if (persistent
) *persistent
= ne
->isPersistentTypeAlias();
1263 bool Unit::defTypeAlias(Id id
) {
1264 assertx(id
< m_typeAliases
.size());
1265 auto thisType
= &m_typeAliases
[id
];
1266 auto nameList
= NamedEntity::get(thisType
->name
);
1267 const StringData
* typeName
= thisType
->value
;
1270 * Check if this name already was defined as a type alias, and if so
1271 * make sure it is compatible.
1273 if (auto current
= nameList
->getCachedTypeAlias()) {
1274 auto raiseIncompatible
= [&] {
1275 FrameRestore
_(this, Op::DefTypeAlias
, id
);
1276 raise_error("The type %s is already defined to an incompatible type",
1277 thisType
->name
->data());
1279 if (nameList
->isPersistentTypeAlias()) {
1280 // We may have cached the fully resolved type in a previous request.
1281 if (resolveTypeAlias(this, thisType
) != *current
) {
1282 raiseIncompatible();
1286 if (!current
->compat(*thisType
)) {
1287 raiseIncompatible();
1292 // There might also be a class with this name already.
1293 if (nameList
->getCachedClass()) {
1294 FrameRestore
_(this, Op::DefTypeAlias
, id
);
1295 raise_error("The name %s is already defined as a class",
1296 thisType
->name
->data());
1300 auto resolved
= resolveTypeAlias(this, thisType
);
1301 if (resolved
.invalid
) {
1302 FrameRestore
_(this, Op::DefTypeAlias
, id
);
1303 raise_error("Unknown type or class %s", typeName
->data());
1307 nameList
->m_cachedTypeAlias
.bind(
1309 auto rdsMode
= [&] {
1310 if (!(thisType
->attrs
& AttrPersistent
)) return rds::Mode::Normal
;
1311 if (resolved
.klass
&& !classHasPersistentRDS(resolved
.klass
)) {
1312 return rds::Mode::Normal
;
1314 return rds::Mode::Persistent
;
1316 auto link
= rds::alloc
<TypeAliasReq
>(rdsMode
);
1317 rds::recordRds(link
.handle(),
1318 sizeof(TypeAliasReq
),
1319 "TypeAlias", typeName
->data());
1320 return link
.handle();
1323 nameList
->setCachedTypeAlias(resolved
);
1324 return nameList
->m_cachedTypeAlias
.isPersistent();
1327 ///////////////////////////////////////////////////////////////////////////////
1331 ///////////////////////////////////////////////////////////////////////////////
1333 SimpleMutex
unitInitLock(false /* reentrant */, RankUnitInit
);
1335 void setGlobal(StringData
* name
, TypedValue
*value
) {
1336 g_context
->m_globalVarEnv
->set(name
, value
);
1339 ///////////////////////////////////////////////////////////////////////////////
1342 void Unit::initialMerge() {
1343 unitInitLock
.assertOwnedBySelf();
1344 if (m_mergeState
.load(std::memory_order_relaxed
) != MergeState::Unmerged
) {
1348 if (RuntimeOption::EvalEnableReverseDataMap
) {
1349 data_map::register_start(this);
1353 bool needsCompact
= false;
1354 m_mergeState
.store(MergeState::Merging
, std::memory_order_relaxed
);
1356 auto const mi
= m_mergeInfo
.load(std::memory_order_relaxed
);
1357 bool allFuncsUnique
= RuntimeOption::RepoAuthoritative
;
1358 for (auto& func
: mi
->nonMainFuncs()) {
1359 if (allFuncsUnique
) {
1360 allFuncsUnique
= (func
->attrs() & AttrUnique
);
1363 if (rds::isPersistentHandle(func
->funcHandle())) {
1364 needsCompact
= true;
1367 if (allFuncsUnique
) state
|= MergeState::UniqueFuncs
;
1369 if (RuntimeOption::RepoAuthoritative
|| !SystemLib::s_inited
) {
1371 * The mergeables array begins with the hoistable Func*s,
1372 * followed by the (potentially) hoistable Class*s.
1374 * If the Unit is merge only, it then contains enough information
1375 * to simulate executing the pseudomain. Normally, this is just
1376 * the Class*s that might not be hoistable. In RepoAuthoritative
1377 * mode it also includes assignments of the form:
1378 * $GLOBALS[string-literal] = scalar;
1379 * defines of the form:
1380 * define(string-literal, scalar);
1383 * These cases are differentiated using the bottom 3 bits
1384 * of the pointer. In the case of a define or a global,
1385 * the pointer will be followed by a TypedValue representing
1386 * the value being defined/assigned.
1388 int ix
= mi
->m_firstHoistablePreClass
;
1389 int end
= mi
->m_firstMergeablePreClass
;
1391 PreClass
* pre
= (PreClass
*)mi
->mergeableObj(ix
++);
1392 if (pre
->attrs() & AttrUnique
) {
1393 needsCompact
= true;
1397 if (isMergeOnly()) {
1398 ix
= mi
->m_firstMergeablePreClass
;
1399 end
= mi
->m_mergeablesSize
;
1401 void *obj
= mi
->mergeableObj(ix
);
1402 auto k
= MergeKind(uintptr_t(obj
) & 7);
1404 case MergeKind::UniqueDefinedClass
:
1405 case MergeKind::Done
:
1407 case MergeKind::TypeAlias
: {
1408 auto const aliasId
= static_cast<Id
>(intptr_t(obj
)) >> 3;
1409 if (m_typeAliases
[aliasId
].attrs
& AttrPersistent
) {
1410 needsCompact
= true;
1414 case MergeKind::Class
:
1415 if (static_cast<PreClass
*>(obj
)->attrs() & AttrUnique
) {
1416 needsCompact
= true;
1419 case MergeKind::ReqDoc
: {
1420 StringData
* s
= (StringData
*)((char*)obj
- (int)k
);
1421 auto const unit
= lookupUnit(
1422 SourceRootInfo::RelativeToPhpRoot(StrNR(s
)).get(),
1424 nullptr /* initial_opt */
1426 unit
->initialMerge();
1427 mi
->mergeableObj(ix
) = (void*)((char*)unit
+ (int)k
);
1430 case MergeKind::PersistentDefine
:
1431 needsCompact
= true;
1432 case MergeKind::Define
: {
1433 auto const s
= (StringData
*)((char*)obj
- (int)k
);
1434 auto const v
= (TypedValueAux
*)mi
->mergeableData(ix
+ 1);
1435 if (k
== MergeKind::PersistentDefine
&& bindPersistentCns(s
, *v
)) {
1436 Stats::inc(Stats::UnitMerge_mergeable
);
1437 Stats::inc(Stats::UnitMerge_mergeable_persistent_define
);
1439 ix
+= sizeof(*v
) / sizeof(void*);
1440 v
->rdsHandle() = makeCnsHandle(s
);
1443 case MergeKind::Global
:
1444 // Skip over the value of the global, embedded in mergeableData
1445 ix
+= sizeof(TypedValueAux
) / sizeof(void*);
1451 if (needsCompact
) state
|= MergeState::NeedsCompact
;
1454 m_mergeState
.store(MergeState::Merged
| state
, std::memory_order_relaxed
);
1457 void Unit::merge() {
1458 if (UNLIKELY(!(m_mergeState
.load(std::memory_order_relaxed
) &
1459 MergeState::Merged
))) {
1460 SimpleLock
lock(unitInitLock
);
1464 if (UNLIKELY(isDebuggerAttached())) {
1465 mergeImpl
<true>(rds::tl_base
, mergeInfo());
1467 mergeImpl
<false>(rds::tl_base
, mergeInfo());
1471 void* Unit::replaceUnit() const {
1472 if (isEmpty()) return nullptr;
1473 if (!isMergeOnly()) return const_cast<Unit
*>(this);
1474 auto const mi
= mergeInfo();
1475 if (mi
->m_mergeablesSize
== mi
->m_firstHoistableFunc
+ 1) {
1477 mi
->mergeableObj(mi
->m_firstHoistableFunc
);
1478 if (mi
->m_firstMergeablePreClass
==
1479 mi
->m_firstHoistableFunc
) {
1480 auto k
= MergeKind(uintptr_t(obj
) & 7);
1481 if (k
!= MergeKind::Class
) return obj
;
1482 } else if (mi
->m_firstHoistablePreClass
==
1483 mi
->m_firstHoistableFunc
) {
1484 if (uintptr_t(obj
) & 1) {
1485 return (char*)obj
- 1 + (int)MergeKind::UniqueDefinedClass
;
1489 return const_cast<Unit
*>(this);
1492 static size_t compactMergeInfo(Unit::MergeInfo
* in
, Unit::MergeInfo
* out
,
1493 const FixedVector
<TypeAlias
>& aliasInfo
) {
1494 using MergeKind
= Unit::MergeKind
;
1496 Func
** it
= in
->funcHoistableBegin();
1497 Func
** fend
= in
->funcEnd();
1499 unsigned ix
, end
, oix
= 0;
1502 if (in
!= out
) memcpy(out
, in
, uintptr_t(it
) - uintptr_t(in
));
1503 iout
= out
->funcHoistableBegin();
1507 while (it
!= fend
) {
1509 if (rds::isPersistentHandle(func
->funcHandle())) {
1517 oix
= out
->m_firstHoistablePreClass
-= delta
;
1520 ix
= in
->m_firstHoistablePreClass
;
1521 end
= in
->m_firstMergeablePreClass
;
1522 for (; ix
< end
; ++ix
) {
1523 void* obj
= in
->mergeableObj(ix
);
1524 assertx((uintptr_t(obj
) & 1) == 0);
1525 PreClass
* pre
= (PreClass
*)obj
;
1526 if (pre
->attrs() & AttrUnique
) {
1527 Class
* cls
= pre
->namedEntity()->clsList();
1528 assertx(cls
&& !cls
->m_nextClass
);
1529 assertx(cls
->preClass() == pre
);
1530 if (rds::isPersistentHandle(cls
->classHandle())) {
1533 out
->mergeableObj(oix
++) = (void*)(uintptr_t(cls
) | 1);
1536 out
->mergeableObj(oix
++) = obj
;
1541 out
->m_firstMergeablePreClass
= oix
;
1544 end
= in
->m_mergeablesSize
;
1546 void* obj
= in
->mergeableObj(ix
++);
1547 auto k
= MergeKind(uintptr_t(obj
) & 7);
1549 case MergeKind::Class
: {
1550 PreClass
* pre
= (PreClass
*)obj
;
1551 if (pre
->attrs() & AttrUnique
) {
1552 Class
* cls
= pre
->namedEntity()->clsList();
1553 assertx(cls
&& !cls
->m_nextClass
);
1554 assertx(cls
->preClass() == pre
);
1555 if (rds::isPersistentHandle(cls
->classHandle())) {
1558 out
->mergeableObj(oix
++) = (void*)
1559 (uintptr_t(cls
) | uintptr_t(MergeKind::UniqueDefinedClass
));
1562 out
->mergeableObj(oix
++) = obj
;
1566 case MergeKind::TypeAlias
: {
1567 auto const aliasId
= static_cast<Id
>(intptr_t(obj
)) >> 3;
1568 if (aliasInfo
[aliasId
].attrs
& AttrPersistent
) {
1571 out
->mergeableObj(oix
++) = obj
;
1575 case MergeKind::UniqueDefinedClass
:
1578 case MergeKind::PersistentDefine
: {
1579 auto const v
= (TypedValueAux
*)in
->mergeableData(ix
);
1580 if (rds::isPersistentHandle(v
->rdsHandle())) {
1581 delta
+= 1 + sizeof(TypedValueAux
) / sizeof(void*);
1582 ix
+= sizeof(TypedValueAux
) / sizeof(void*);
1585 obj
= (char*)obj
- (int)MergeKind::PersistentDefine
+
1586 (int)MergeKind::Define
;
1589 case MergeKind::Define
:
1590 case MergeKind::Global
:
1592 out
->mergeableObj(oix
++) = obj
;
1593 *(TypedValueAux
*)out
->mergeableData(oix
) =
1594 *(TypedValueAux
*)in
->mergeableData(ix
);
1595 oix
+= sizeof(TypedValueAux
) / sizeof(void*);
1597 ix
+= sizeof(TypedValueAux
) / sizeof(void*);
1600 case MergeKind::ReqDoc
: {
1601 Unit
*unit
= (Unit
*)((char*)obj
- (int)k
);
1602 void *rep
= unit
->replaceUnit();
1607 out
->mergeableObj(oix
++) = obj
;
1609 out
->mergeableObj(oix
++) = rep
;
1614 case MergeKind::Done
:
1619 // copy the MergeKind::Done marker
1620 out
->mergeableObj(oix
) = in
->mergeableObj(ix
);
1621 out
->m_mergeablesSize
= oix
;
1626 template <bool debugger
>
1627 void Unit::mergeImpl(void* tcbase
, MergeInfo
* mi
) {
1628 assertx(m_mergeState
.load(std::memory_order_relaxed
) & MergeState::Merged
);
1630 autoTypecheck(this);
1632 Func
** it
= mi
->funcHoistableBegin();
1633 Func
** fend
= mi
->funcEnd();
1635 if (LIKELY((m_mergeState
.load(std::memory_order_relaxed
) &
1636 MergeState::UniqueFuncs
) != 0)) {
1639 assertx(func
->top());
1640 assertx(func
->isUnique());
1641 auto const handle
= func
->funcHandle();
1642 getDataRef
<LowPtr
<Func
>>(tcbase
, handle
) = func
;
1643 if (rds::isNormalHandle(handle
)) rds::initHandle(handle
);
1644 func
->getNamedEntity()->setUniqueFunc(func
);
1645 if (debugger
) phpDebuggerDefFuncHook(func
);
1646 } while (++it
!= fend
);
1650 assertx(func
->top());
1651 defFunc(func
, debugger
);
1652 } while (++it
!= fend
);
1656 bool redoHoistable
= false;
1657 int ix
= mi
->m_firstHoistablePreClass
;
1658 int end
= mi
->m_firstMergeablePreClass
;
1659 // iterate over all the potentially hoistable classes
1660 // with no fatals on failure
1663 // The first time this unit is merged, if the classes turn out to be all
1664 // unique and defined, we replace the PreClass*'s with the corresponding
1665 // Class*'s, with the low-order bit marked.
1666 PreClass
* pre
= (PreClass
*)mi
->mergeableObj(ix
);
1667 if (LIKELY(uintptr_t(pre
) & 1)) {
1668 Stats::inc(Stats::UnitMerge_hoistable
);
1669 Class
* cls
= (Class
*)(uintptr_t(pre
) & ~1);
1670 if (cls
->isPersistent()) {
1671 Stats::inc(Stats::UnitMerge_hoistable_persistent
);
1673 if (Stats::enabled() &&
1674 rds::isPersistentHandle(cls
->classHandle())) {
1675 Stats::inc(Stats::UnitMerge_hoistable_persistent_cache
);
1677 if (Class
* parent
= cls
->parent()) {
1678 if (parent
->isPersistent()) {
1679 Stats::inc(Stats::UnitMerge_hoistable_persistent_parent
);
1681 if (Stats::enabled() &&
1682 rds::isPersistentHandle(parent
->classHandle())) {
1683 Stats::inc(Stats::UnitMerge_hoistable_persistent_parent_cache
);
1686 auto const parent_handle
= parent
->classHandle();
1687 auto const parent_cls_present
=
1688 rds::isHandleInit(parent_handle
) &&
1689 getDataRef
<LowPtr
<Class
>>(tcbase
, parent_handle
);
1690 if (UNLIKELY(!parent_cls_present
)) {
1691 redoHoistable
= true;
1695 auto const handle
= cls
->classHandle();
1696 getDataRef
<LowPtr
<Class
>>(tcbase
, handle
) = cls
;
1697 if (rds::isNormalHandle(handle
)) rds::initHandle(handle
);
1698 if (debugger
) phpDebuggerDefClassHook(cls
);
1700 if (UNLIKELY(!defClass(pre
, false))) {
1701 redoHoistable
= true;
1704 } while (++ix
< end
);
1706 if (UNLIKELY(redoHoistable
)) {
1707 // if this unit isnt mergeOnly, we're done
1708 if (!isMergeOnly()) return;
1710 // As a special case, if all the classes are potentially hoistable, we
1711 // don't list them twice, but instead iterate over them again.
1713 // At first glance, it may seem like we could leave the maybe-hoistable
1714 // classes out of the second list and then always reset ix to 0; but that
1715 // gets this case wrong if there's an autoloader for C, and C extends B:
1718 // class B implements I {}
1719 // class D extends C {}
1721 // because now A and D go on the maybe-hoistable list B goes on the never
1722 // hoistable list, and we fatal trying to instantiate D before B
1723 Stats::inc(Stats::UnitMerge_redo_hoistable
);
1724 if (end
== (int)mi
->m_mergeablesSize
) {
1725 ix
= mi
->m_firstHoistablePreClass
;
1727 void* obj
= mi
->mergeableObj(ix
);
1728 if (UNLIKELY(uintptr_t(obj
) & 1)) {
1729 Class
* cls
= (Class
*)(uintptr_t(obj
) & ~1);
1730 defClass(cls
->preClass(), true);
1732 defClass((PreClass
*)obj
, true);
1734 } while (++ix
< end
);
1740 // iterate over all but the guaranteed hoistable classes
1741 // fataling if we fail.
1742 void* obj
= mi
->mergeableObj(ix
);
1743 auto k
= MergeKind(uintptr_t(obj
) & 7);
1746 case MergeKind::Class
:
1748 Stats::inc(Stats::UnitMerge_mergeable
);
1749 Stats::inc(Stats::UnitMerge_mergeable_class
);
1750 defClass((PreClass
*)obj
, true);
1751 obj
= mi
->mergeableObj(++ix
);
1752 k
= MergeKind(uintptr_t(obj
) & 7);
1753 } while (k
== MergeKind::Class
);
1756 case MergeKind::UniqueDefinedClass
:
1758 Stats::inc(Stats::UnitMerge_mergeable
);
1759 Stats::inc(Stats::UnitMerge_mergeable_unique
);
1760 Class
* other
= nullptr;
1761 Class
* cls
= (Class
*)((char*)obj
- (int)k
);
1762 if (cls
->isPersistent()) {
1763 Stats::inc(Stats::UnitMerge_mergeable_unique_persistent
);
1765 if (Stats::enabled() &&
1766 rds::isPersistentHandle(cls
->classHandle())) {
1767 Stats::inc(Stats::UnitMerge_mergeable_unique_persistent_cache
);
1769 Class::Avail avail
= cls
->avail(other
, true);
1770 if (UNLIKELY(avail
== Class::Avail::Fail
)) {
1771 raise_error("unknown class %s", other
->name()->data());
1773 assertx(avail
== Class::Avail::True
);
1774 auto const handle
= cls
->classHandle();
1775 getDataRef
<LowPtr
<Class
>>(tcbase
, handle
) = cls
;
1776 if (rds::isNormalHandle(handle
)) rds::initHandle(handle
);
1777 if (debugger
) phpDebuggerDefClassHook(cls
);
1778 obj
= mi
->mergeableObj(++ix
);
1779 k
= MergeKind(uintptr_t(obj
) & 7);
1780 } while (k
== MergeKind::UniqueDefinedClass
);
1783 case MergeKind::PersistentDefine
:
1784 // will be removed by compactMergeInfo but will be hit at
1785 // least once before that happens.
1787 auto const v
= (TypedValueAux
*)mi
->mergeableData(ix
+ 1);
1788 if (!rds::isPersistentHandle(v
->rdsHandle())) {
1789 obj
= (char*)obj
- (int)MergeKind::PersistentDefine
+
1790 (int)MergeKind::Define
;
1791 k
= MergeKind::Define
;
1794 ix
+= 1 + sizeof(TypedValueAux
) / sizeof(void*);
1795 obj
= mi
->mergeableObj(ix
);
1796 k
= MergeKind(uintptr_t(obj
) & 7);
1797 } while (k
== MergeKind::PersistentDefine
);
1800 case MergeKind::Define
:
1802 Stats::inc(Stats::UnitMerge_mergeable
);
1803 Stats::inc(Stats::UnitMerge_mergeable_define
);
1805 auto const name
= (StringData
*)((char*)obj
- (int)k
);
1806 auto const v
= (TypedValueAux
*)mi
->mergeableData(ix
+ 1);
1807 assertx(v
->m_type
!= KindOfUninit
);
1809 auto const handle
= v
->rdsHandle();
1810 assertx(rds::isNormalHandle(handle
));
1811 if (UNLIKELY(rds::isHandleInit(handle
, rds::NormalTag
{}))) {
1812 raise_notice(Strings::CONSTANT_ALREADY_DEFINED
, name
->data());
1814 getDataRef
<TypedValue
>(tcbase
, handle
) = *v
;
1815 rds::initHandle(handle
);
1817 ix
+= 1 + sizeof(*v
) / sizeof(void*);
1818 obj
= mi
->mergeableObj(ix
);
1819 k
= MergeKind(uintptr_t(obj
) & 7);
1820 } while (k
== MergeKind::Define
);
1823 case MergeKind::Global
:
1825 Stats::inc(Stats::UnitMerge_mergeable
);
1826 Stats::inc(Stats::UnitMerge_mergeable_global
);
1827 StringData
* name
= (StringData
*)((char*)obj
- (int)k
);
1828 auto* v
= (TypedValueAux
*)mi
->mergeableData(ix
+ 1);
1830 ix
+= 1 + sizeof(*v
) / sizeof(void*);
1831 obj
= mi
->mergeableObj(ix
);
1832 k
= MergeKind(uintptr_t(obj
) & 7);
1833 } while (k
== MergeKind::Global
);
1836 case MergeKind::ReqDoc
:
1838 Stats::inc(Stats::UnitMerge_mergeable
);
1839 Stats::inc(Stats::UnitMerge_mergeable_require
);
1840 Unit
*unit
= (Unit
*)((char*)obj
- (int)k
);
1842 unit
->mergeImpl
<debugger
>(tcbase
, unit
->mergeInfo());
1843 if (UNLIKELY(!unit
->isMergeOnly())) {
1844 Stats::inc(Stats::PseudoMain_Reentered
);
1845 VarEnv
* ve
= nullptr;
1846 ActRec
* fp
= vmfp();
1848 ve
= g_context
->m_globalVarEnv
;
1850 if ((fp
->func()->attrs() & AttrMayUseVV
) && fp
->hasVarEnv()) {
1853 // Nothing to do. If there is no varEnv, the enclosing
1854 // file was called by fb_autoload_map, which wants a
1859 g_context
->invokeFunc(unit
->getMain(nullptr),
1861 nullptr, nullptr, ve
)
1864 Stats::inc(Stats::PseudoMain_SkipDeep
);
1867 obj
= mi
->mergeableObj(++ix
);
1868 k
= MergeKind(uintptr_t(obj
) & 7);
1869 } while (k
== MergeKind::ReqDoc
);
1871 case MergeKind::TypeAlias
:
1873 Stats::inc(Stats::UnitMerge_mergeable
);
1874 Stats::inc(Stats::UnitMerge_mergeable_typealias
);
1875 auto const aliasId
= static_cast<Id
>(intptr_t(obj
)) >> 3;
1876 if (!defTypeAlias(aliasId
)) {
1877 auto& attrs
= m_typeAliases
[aliasId
].attrs
;
1878 if (attrs
& AttrPersistent
) {
1879 attrs
= static_cast<Attr
>(attrs
& ~AttrPersistent
);
1882 obj
= mi
->mergeableObj(++ix
);
1883 k
= MergeKind(uintptr_t(obj
) & 7);
1884 } while (k
== MergeKind::TypeAlias
);
1886 case MergeKind::Done
:
1887 assertx((unsigned)ix
== mi
->m_mergeablesSize
);
1888 if (UNLIKELY(m_mergeState
.load(std::memory_order_relaxed
) &
1889 MergeState::NeedsCompact
)) {
1890 SimpleLock
lock(unitInitLock
);
1891 if (!(m_mergeState
.load(std::memory_order_relaxed
) &
1892 MergeState::NeedsCompact
)) {
1895 if (!redoHoistable
) {
1897 * All the classes are known to be unique, and we just got
1898 * here, so all were successfully defined. We can now go
1899 * back and convert all MergeKind::Class entries to
1900 * MergeKind::UniqueDefinedClass, and all hoistable
1901 * classes to their Class*'s instead of PreClass*'s.
1903 * We can also remove any Persistent Class/Func*'s,
1904 * and any requires of modules that are (now) empty
1906 size_t delta
= compactMergeInfo(mi
, nullptr, m_typeAliases
);
1907 MergeInfo
* newMi
= mi
;
1909 newMi
= MergeInfo::alloc(mi
->m_mergeablesSize
- delta
);
1912 * In the case where mi == newMi, there's an apparent
1913 * race here. Although we have a lock, so we're the only
1914 * ones modifying this, there could be any number of
1915 * readers. But thats ok, because it doesnt matter
1916 * whether they see the old contents or the new.
1918 compactMergeInfo(mi
, newMi
, m_typeAliases
);
1920 this->m_mergeInfo
.store(newMi
, std::memory_order_release
);
1921 Treadmill::deferredFree(mi
);
1922 if (isMergeOnly() &&
1923 newMi
->m_firstHoistableFunc
== newMi
->m_mergeablesSize
) {
1924 m_mergeState
.fetch_or(MergeState::Empty
,
1925 std::memory_order_relaxed
);
1928 assertx(newMi
->m_firstMergeablePreClass
1929 == newMi
->m_mergeablesSize
||
1932 m_mergeState
.fetch_and(~MergeState::NeedsCompact
,
1933 std::memory_order_relaxed
);
1937 // Normal cases should continue, KindDone returns
1942 ///////////////////////////////////////////////////////////////////////////////
1947 Array
getClassesWithAttrInfo(Attr attrs
, bool inverse
= false) {
1948 Array a
= Array::Create();
1949 NamedEntity::foreach_cached_class([&](Class
* c
) {
1950 if ((c
->attrs() & attrs
) ? !inverse
: inverse
) {
1951 if (c
->isBuiltin()) {
1952 a
.prepend(make_tv
<KindOfPersistentString
>(c
->name()));
1954 a
.append(make_tv
<KindOfPersistentString
>(c
->name()));
1961 template<bool system
>
1962 Array
getFunctions() {
1963 // Return an array of all defined functions. This method is used
1964 // to support get_defined_functions().
1965 Array a
= Array::Create();
1966 NamedEntity::foreach_cached_func([&](Func
* func
) {
1967 if ((system
^ func
->isBuiltin()) || func
->isGenerated()) return; //continue
1968 a
.append(HHVM_FN(strtolower
)(func
->nameStr()));
1973 ///////////////////////////////////////////////////////////////////////////////
1976 Array
Unit::getClassesInfo() {
1977 return getClassesWithAttrInfo(AttrInterface
| AttrTrait
,
1978 /* inverse = */ true);
1981 Array
Unit::getInterfacesInfo() {
1982 return getClassesWithAttrInfo(AttrInterface
);
1985 Array
Unit::getTraitsInfo() {
1986 return getClassesWithAttrInfo(AttrTrait
);
1989 Array
Unit::getUserFunctions() {
1990 return getFunctions
<false>();
1993 Array
Unit::getSystemFunctions() {
1994 return getFunctions
<true>();
1998 ///////////////////////////////////////////////////////////////////////////////
2001 void Unit::prettyPrint(std::ostream
& out
, PrintOpts opts
) const {
2002 auto startOffset
= opts
.startOffset
!= kInvalidOffset
2003 ? opts
.startOffset
: 0;
2004 auto stopOffset
= opts
.stopOffset
!= kInvalidOffset
2005 ? opts
.stopOffset
: m_bclen
;
2007 std::map
<Offset
,const Func
*> funcMap
;
2008 for (auto& func
: funcs()) {
2009 funcMap
[func
->base()] = func
;
2011 for (auto it
= m_preClasses
.begin();
2012 it
!= m_preClasses
.end(); ++it
) {
2013 Func
* const* methods
= (*it
)->methods();
2014 size_t const numMethods
= (*it
)->numMethods();
2015 for (size_t i
= 0; i
< numMethods
; ++i
) {
2016 funcMap
[methods
[i
]->base()] = methods
[i
];
2020 auto funcIt
= funcMap
.lower_bound(startOffset
);
2022 const auto* it
= &m_bc
[startOffset
];
2023 int prevLineNum
= -1;
2024 while (it
< &m_bc
[stopOffset
]) {
2025 if (opts
.showFuncs
) {
2026 assertx(funcIt
== funcMap
.end() || funcIt
->first
>= offsetOf(it
));
2027 if (funcIt
!= funcMap
.end() &&
2028 funcIt
->first
== offsetOf(it
)) {
2030 funcIt
->second
->prettyPrint(out
);
2036 if (opts
.showLines
) {
2037 int lineNum
= getLineNumber(offsetOf(it
));
2038 if (lineNum
!= prevLineNum
) {
2039 out
<< " // line " << lineNum
<< std::endl
;
2040 prevLineNum
= lineNum
;
2044 out
<< std::string(opts
.indentSize
, ' ')
2045 << std::setw(4) << (it
- m_bc
) << ": "
2046 << instrToString(it
, this)
2052 std::string
Unit::toString() const {
2053 std::ostringstream ss
;
2055 for (auto& pc
: m_preClasses
) {
2056 pc
->prettyPrint(ss
);
2058 for (auto& func
: funcs()) {
2059 func
->prettyPrint(ss
);
2065 ///////////////////////////////////////////////////////////////////////////////
2068 bool Unit::compileTimeFatal(const StringData
*& msg
, int& line
) const {
2069 auto entry
= getMain(nullptr)->getEntry();
2071 // String <id>; Fatal;
2073 if (decode_op(pc
) != Op::String
) {
2076 // String <id>; Fatal;
2080 // String <id>; Fatal;
2082 if (decode_op(pc
) != Op::Fatal
) {
2085 msg
= lookupLitstrId(id
);
2086 line
= getLineNumber(Offset(pc
- entry
));
2090 bool Unit::parseFatal(const StringData
*& msg
, int& line
) const {
2091 if (!compileTimeFatal(msg
, line
)) {
2095 auto pc
= getMain(nullptr)->getEntry();
2103 auto kind_char
= *pc
;
2104 return kind_char
== static_cast<uint8_t>(FatalOp::Parse
);