Codemod asserts to assertxs in the runtime
[hiphop-php.git] / hphp / runtime / vm / unit.cpp
blob41e1c1f485bfd31096ef81f7ce8c77384c442492
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
19 #include <algorithm>
20 #include <atomic>
21 #include <cstdlib>
22 #include <cstring>
23 #include <iomanip>
24 #include <map>
25 #include <ostream>
26 #include <sstream>
27 #include <vector>
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"
89 namespace HPHP {
91 //////////////////////////////////////////////////////////////////////
93 namespace {
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
106 template<class T>
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
129 * encompasses.
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
133 * sub-statements.
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<
142 const Unit*,
143 ExtendedLineInfo,
144 pointer_hash<Unit>
146 ExtendedLineInfoCache s_extendedLineInfo;
148 using LineTableStash = tbb::concurrent_hash_map<
149 const Unit*,
150 LineTable,
151 pointer_hash<Unit>
153 LineTableStash s_lineTables;
155 struct LineCacheEntry {
156 LineCacheEntry(const Unit* unit, LineTable&& table)
157 : unit{unit}
158 , table{std::move(table)}
160 const Unit* unit;
161 LineTable table;
163 std::array<std::atomic<LineCacheEntry*>, 512> s_lineCache;
165 //////////////////////////////////////////////////////////////////////
169 ///////////////////////////////////////////////////////////////////////////////
170 // MergeInfo.
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;
179 return mi;
183 ///////////////////////////////////////////////////////////////////////////////
184 // Construction and destruction.
186 Unit::Unit()
187 : m_mergeOnly(false)
188 , m_interpretOnly(false)
189 , m_isHHFile(false)
190 , m_extended(false)
191 , m_mainReturn(make_tv<KindOfUninit>())
194 Unit::~Unit() {
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) {
215 if (debug) {
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();
224 if (mi) {
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();
232 while (cls) {
233 Class* cur = cls;
234 cls = cls->m_nextClass;
235 if (cur->preClass() == pcls.get()) {
236 cur->destroy();
241 free(mi);
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*/) {
256 low_free_data(p);
260 ///////////////////////////////////////////////////////////////////////////////
261 // Code locations.
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);
270 return ret;
274 * Return the Unit's SourceLocTable, extracting it from the repo if
275 * necessary.
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.
297 * Algorithm:
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(
308 const Unit* unit,
309 LineToOffsetRangeVecMap& map
311 // First generate an OffsetRange for each SourceLoc.
312 auto const& srcLocTable = getSourceLocTable(unit);
314 struct LineRange {
315 LineRange(int start, int end)
316 : line0(start), line1(end)
318 int line0;
319 int line1;
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;
328 Offset baseOff = 0;
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);
334 baseOff = pastOff;
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.
340 std::sort(
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);
361 } else {
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.
372 std::sort(
373 entry.second.begin(),
374 entry.second.end(),
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 "
411 "have been");
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)) {
423 return acc->second;
425 static LineTable empty;
426 return 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; });
445 return p->table;
448 static LineInfo getLineInfo(const LineTable& table, Offset pc) {
449 auto const it =
450 std::upper_bound(begin(table), end(table), LineEntry{ pc, -1 });
452 auto const e = end(table);
453 if (it != e) {
454 auto const line = it->val();
455 if (line > 0) {
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());
471 return it->val();
473 return -1;
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)) {
481 return &acc->second;
483 return nullptr;
484 }();
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;
501 return INT_MIN;
504 auto line = findLine();
505 if (line != INT_MIN) return line;
507 m_lineMap.lock_for_update();
508 try {
509 line = findLine();
510 if (line != INT_MIN) {
511 m_lineMap.unlock();
512 return line;
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(),
519 info,
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(); });
528 return info.second;
529 } catch (...) {
530 m_lineMap.unlock();
531 throw;
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());
540 sLoc = it->val();
541 return true;
543 return false;
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) {
558 range = offset;
559 return true;
562 return false;
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;
571 return true;
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());
589 return *it;
591 return nullptr;
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()) {
620 return it->second;
622 Func* f = (*mi->funcBegin())->clone(cls);
623 f->setNewFuncId();
624 f->setBaseCls(cls);
625 (*m_pseudoMainCache)[cls] = f;
626 return 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
633 // beginning
634 assertx(oldName && oldName->isStatic());
635 assertx(newName && newName->isStatic());
637 for (auto& func : mergeInfo()->hoistableFuncs()) {
638 auto const name = func->name();
639 assertx(name);
640 if (name->same(oldName)) {
641 func->rename(newName);
642 break;
648 ///////////////////////////////////////////////////////////////////////////////
649 // Func lookup.
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());
663 } else {
664 assertx(rds::isNormalHandle(handle));
665 if (!rds::isHandleInit(handle, rds::NormalTag{})) {
666 rds::initHandle(handle);
667 } else {
668 if (funcAddr.get() == func) return;
669 raise_error(Strings::FUNCTION_ALREADY_DEFINED, func->name()->data());
672 funcAddr = func;
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
692 // NamedEntity.
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();
705 return func;
708 Func* Unit::loadFunc(const StringData* name) {
709 String normStr;
710 auto ne = NamedEntity::get(name, true, &normStr);
711 if (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(
721 [&] {
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);
727 *link = func;
728 if (func->isUnique()) ne->setUniqueFunc(func);
729 if (RuntimeOption::EvalPerfDataMap) {
730 rds::recordRds(
731 link.handle(),
732 sizeof(void*),
733 "Func",
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) {
751 ActRec* fp = vmfp();
753 if (vmsp() && (!fp || fp->m_func->unit() != unit)) {
754 m_top = vmsp();
755 m_fp = fp;
756 m_pc = vmpc();
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()
764 is NULL
766 ActRec &tmp = *vmStack().allocA();
767 tmp.m_sfp = fp;
768 tmp.m_savedRip = 0;
769 tmp.m_func = unit->getMain(nullptr);
770 tmp.m_soff = !fp
772 : fp->m_func->unit()->offsetOf(m_pc) - fp->m_func->base();
773 tmp.trashThis();
774 tmp.m_varEnv = 0;
775 tmp.initNumArgs(0);
776 vmfp() = &tmp;
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());
783 while (pc < past) {
784 if (peek_op(pc) == op) {
785 auto tpc = pc;
786 decode_op(tpc);
787 if (decode_iva(tpc) == id) return unit->offsetOf(pc);
789 pc += instrLen(pc);
791 return tmp.m_func->base();
792 }();
793 vmpc() = unit->at(offset);
794 pushFrameSlots(tmp.m_func);
795 } else {
796 m_top = nullptr;
797 m_fp = nullptr;
798 m_pc = nullptr;
801 ~FrameRestore() {
802 if (m_top) {
803 vmsp() = m_top;
804 vmfp() = m_fp;
805 vmpc() = m_pc;
808 private:
809 Cell* m_top;
810 ActRec* m_fp;
811 PC m_pc;
814 ///////////////////////////////////////////////////////////////////////////////
815 // Class lookup.
817 namespace {
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
848 * NamedEntity.
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());
857 return nullptr;
860 // If there was already a class declared with DefClass, check if it's
861 // compatible.
862 if (Class* cls = nameList->getCachedClass()) {
863 if (cls->preClass() != preClass) {
864 if (failIsFatal) {
865 FrameRestore fr(preClass);
866 raise_error("Class already declared: %s", preClass->name()->data());
868 return nullptr;
870 return cls;
873 // Get a compatible Class, and add it to the list of defined classes.
874 Class* parent = nullptr;
875 for (;;) {
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; ) {
880 Class* cur = class_;
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)) {
885 cur->setCached();
886 DEBUGGER_ATTACHED_ONLY(phpDebuggerDefClassHook(cur));
887 return cur;
889 if (avail == Class::Avail::Fail) {
890 if (failIsFatal) {
891 FrameRestore fr(preClass);
892 raise_error("unknown class %s", parent->name()->data());
894 return nullptr;
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) {
903 if (failIsFatal) {
904 FrameRestore fr(preClass);
905 raise_error("unknown class %s", preClass->parent()->data());
907 return nullptr;
911 ClassPtr newClass;
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();
920 continue;
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.
946 ClassPtr newClass {
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();
960 namespace {
961 bool isPHP7ReservedType(const StringData* alias) {
962 return
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,
971 bool autoload) {
972 if (RuntimeOption::PHP7_ScalarTypes && isPHP7ReservedType(alias)) {
973 raise_error("Fatal error: Cannot use '%s' as class name as it is reserved",
974 alias->data());
976 auto const origClass =
977 autoload ? Unit::loadClass(original)
978 : Unit::lookupClass(original);
979 if (!origClass) {
980 raise_warning("Class %s not found", original->data());
981 return false;
983 if (origClass->isBuiltin()) {
984 raise_warning("First argument of class_alias() must be "
985 "the name of a user defined class");
986 return false;
989 auto const aliasNe = NamedEntity::get(alias);
990 aliasNe->m_cachedClass.bind();
992 auto const aliasClass = aliasNe->getCachedClass();
993 if (aliasClass) {
994 raise_warning("Cannot redeclare class %s", alias->data());
995 return false;
997 aliasNe->setCachedClass(origClass);
998 return true;
1001 Class* Unit::loadClass(const NamedEntity* ne,
1002 const StringData* name) {
1003 Class* cls;
1004 if (LIKELY((cls = ne->getCachedClass()) != nullptr)) {
1005 return cls;
1007 return loadMissingClass(ne, name);
1010 Class* Unit::loadMissingClass(const NamedEntity* ne,
1011 const StringData* name) {
1012 VMRegAnchor _;
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)) {
1022 if (tryAutoload) {
1023 return loadMissingClass(ne, name);
1026 return cls;
1029 bool Unit::classExists(const StringData* name, bool autoload, ClassKind kind) {
1030 Class* cls = Unit::getClass(name, autoload);
1031 return cls &&
1032 (cls->attrs() & (AttrInterface | AttrTrait)) == classKindAsAttr(kind);
1036 ///////////////////////////////////////////////////////////////////////////////
1037 // Constant lookup.
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));
1048 return &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)) {
1057 return tvRet;
1059 return nullptr;
1061 if (UNLIKELY(rds::s_constants().get() != nullptr)) {
1062 return rds::s_constants()->rval(cnsName).tv_ptr();
1064 return nullptr;
1067 const Cell* Unit::lookupPersistentCns(const StringData* cnsName) {
1068 auto const handle = lookupCnsHandle(cnsName);
1069 if (!rds::isHandleBound(handle) || !rds::isPersistentHandle(handle)) {
1070 return nullptr;
1072 auto const ret = &rds::handleToRef<TypedValue>(handle);
1073 assertx(cellIsPlausible(*ret));
1074 return 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))) {
1087 return nullptr;
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());
1105 return false;
1108 if (UNLIKELY(!tvAsCVarRef(value).isAllowedAsConstantValue())) {
1109 raise_warning(Strings::CONSTANTS_MUST_BE_SCALAR);
1110 return false;
1113 assertx(rds::isNormalHandle(ch));
1114 cellDup(*value, *cns);
1115 rds::initHandle(ch);
1116 return true;
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
1127 * optimizing for.
1129 rds::s_constants() =
1130 Array::attach(PackedArray::MakeReserve(PackedArray::SmallSize));
1132 auto const existed = !!rds::s_constants()->rval(cnsName);
1133 if (!existed) {
1134 rds::s_constants().set(StrNR(cnsName),
1135 tvAsCVarRef(value), true /* isKey */);
1136 return true;
1138 raise_notice(Strings::CONSTANT_ALREADY_DEFINED, cnsName->data());
1139 return false;
1141 return defCnsHelper(handle, value, cnsName);
1144 bool Unit::defNativeConstantCallback(const StringData* cnsName,
1145 Cell value) {
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)))) {
1152 return false;
1154 bindPersistentCns(cnsName, value);
1155 return true;
1158 ///////////////////////////////////////////////////////////////////////////////
1159 // Type aliases.
1161 namespace {
1163 TypeAliasReq typeAliasFromClass(Unit* unit, const TypeAlias* thisType,
1164 Class *klass) {
1165 assertx(unit);
1166 TypeAliasReq req;
1167 req.unit = unit;
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);
1174 } else {
1175 req.type = AnnotType::Mixed;
1177 } else {
1178 req.type = AnnotType::Object;
1179 req.klass = klass;
1181 req.userAttrs = thisType->userAttrs;
1182 assertx(thisType->typeStructure.isDictOrDArray());
1183 req.typeStructure = thisType->typeStructure;
1184 return req;
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
1194 * first.
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
1207 * autoloader.
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))
1230 )) {
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,
1246 bool* persistent) {
1247 auto ne = NamedEntity::get(name);
1248 auto target = ne->getCachedTypeAlias();
1249 if (!target) {
1250 if (AutoloadHandler::s_instance->autoloadClassOrType(
1251 StrNR(const_cast<StringData*>(name))
1252 )) {
1253 target = ne->getCachedTypeAlias();
1254 } else {
1255 return nullptr;
1259 if (persistent) *persistent = ne->isPersistentTypeAlias();
1260 return target;
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();
1284 return true;
1286 if (!current->compat(*thisType)) {
1287 raiseIncompatible();
1289 return false;
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());
1297 not_reached();
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());
1304 not_reached();
1307 nameList->m_cachedTypeAlias.bind(
1308 [&] {
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;
1315 }();
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 ///////////////////////////////////////////////////////////////////////////////
1328 // Merge.
1330 namespace {
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) {
1345 return;
1348 if (RuntimeOption::EvalEnableReverseDataMap) {
1349 data_map::register_start(this);
1352 int state = 0;
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);
1362 bindFunc(func);
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);
1381 * and requires.
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;
1390 while (ix < end) {
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;
1400 while (ix < end) {
1401 void *obj = mi->mergeableObj(ix);
1402 auto k = MergeKind(uintptr_t(obj) & 7);
1403 switch (k) {
1404 case MergeKind::UniqueDefinedClass:
1405 case MergeKind::Done:
1406 not_reached();
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;
1412 break;
1414 case MergeKind::Class:
1415 if (static_cast<PreClass*>(obj)->attrs() & AttrUnique) {
1416 needsCompact = true;
1418 break;
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);
1428 break;
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);
1441 break;
1443 case MergeKind::Global:
1444 // Skip over the value of the global, embedded in mergeableData
1445 ix += sizeof(TypedValueAux) / sizeof(void*);
1446 break;
1448 ix++;
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);
1461 initialMerge();
1464 if (UNLIKELY(isDebuggerAttached())) {
1465 mergeImpl<true>(rds::tl_base, mergeInfo());
1466 } else {
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) {
1476 void* obj =
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();
1498 Func** iout = 0;
1499 unsigned ix, end, oix = 0;
1501 if (out) {
1502 if (in != out) memcpy(out, in, uintptr_t(it) - uintptr_t(in));
1503 iout = out->funcHoistableBegin();
1506 size_t delta = 0;
1507 while (it != fend) {
1508 Func* func = *it++;
1509 if (rds::isPersistentHandle(func->funcHandle())) {
1510 delta++;
1511 } else if (iout) {
1512 *iout++ = func;
1516 if (out) {
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())) {
1531 delta++;
1532 } else if (out) {
1533 out->mergeableObj(oix++) = (void*)(uintptr_t(cls) | 1);
1535 } else if (out) {
1536 out->mergeableObj(oix++) = obj;
1540 if (out) {
1541 out->m_firstMergeablePreClass = oix;
1544 end = in->m_mergeablesSize;
1545 while (ix < end) {
1546 void* obj = in->mergeableObj(ix++);
1547 auto k = MergeKind(uintptr_t(obj) & 7);
1548 switch (k) {
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())) {
1556 delta++;
1557 } else if (out) {
1558 out->mergeableObj(oix++) = (void*)
1559 (uintptr_t(cls) | uintptr_t(MergeKind::UniqueDefinedClass));
1561 } else if (out) {
1562 out->mergeableObj(oix++) = obj;
1564 break;
1566 case MergeKind::TypeAlias: {
1567 auto const aliasId = static_cast<Id>(intptr_t(obj)) >> 3;
1568 if (aliasInfo[aliasId].attrs & AttrPersistent) {
1569 delta++;
1570 } else if (out) {
1571 out->mergeableObj(oix++) = obj;
1573 break;
1575 case MergeKind::UniqueDefinedClass:
1576 not_reached();
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*);
1583 break;
1585 obj = (char*)obj - (int)MergeKind::PersistentDefine +
1586 (int)MergeKind::Define;
1587 // fall through
1589 case MergeKind::Define:
1590 case MergeKind::Global:
1591 if (out) {
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*);
1598 break;
1600 case MergeKind::ReqDoc: {
1601 Unit *unit = (Unit*)((char*)obj - (int)k);
1602 void *rep = unit->replaceUnit();
1603 if (!rep) {
1604 delta++;
1605 } else if (out) {
1606 if (rep == unit) {
1607 out->mergeableObj(oix++) = obj;
1608 } else {
1609 out->mergeableObj(oix++) = rep;
1612 break;
1614 case MergeKind::Done:
1615 not_reached();
1618 if (out) {
1619 // copy the MergeKind::Done marker
1620 out->mergeableObj(oix) = in->mergeableObj(ix);
1621 out->m_mergeablesSize = oix;
1623 return delta;
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();
1634 if (it != fend) {
1635 if (LIKELY((m_mergeState.load(std::memory_order_relaxed) &
1636 MergeState::UniqueFuncs) != 0)) {
1637 do {
1638 Func* func = *it;
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);
1647 } else {
1648 do {
1649 Func* func = *it;
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
1661 if (ix < end) {
1662 do {
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;
1692 continue;
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);
1699 } else {
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:
1717 // class A {}
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;
1726 do {
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);
1731 } else {
1732 defClass((PreClass*)obj, true);
1734 } while (++ix < end);
1735 return;
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);
1744 do {
1745 switch (k) {
1746 case MergeKind::Class:
1747 do {
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);
1754 continue;
1756 case MergeKind::UniqueDefinedClass:
1757 do {
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);
1781 continue;
1783 case MergeKind::PersistentDefine:
1784 // will be removed by compactMergeInfo but will be hit at
1785 // least once before that happens.
1786 do {
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;
1792 break;
1794 ix += 1 + sizeof(TypedValueAux) / sizeof(void*);
1795 obj = mi->mergeableObj(ix);
1796 k = MergeKind(uintptr_t(obj) & 7);
1797 } while (k == MergeKind::PersistentDefine);
1798 continue;
1800 case MergeKind::Define:
1801 do {
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());
1813 } else {
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);
1821 continue;
1823 case MergeKind::Global:
1824 do {
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);
1829 setGlobal(name, v);
1830 ix += 1 + sizeof(*v) / sizeof(void*);
1831 obj = mi->mergeableObj(ix);
1832 k = MergeKind(uintptr_t(obj) & 7);
1833 } while (k == MergeKind::Global);
1834 continue;
1836 case MergeKind::ReqDoc:
1837 do {
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();
1847 if (!fp) {
1848 ve = g_context->m_globalVarEnv;
1849 } else {
1850 if ((fp->func()->attrs() & AttrMayUseVV) && fp->hasVarEnv()) {
1851 ve = fp->m_varEnv;
1852 } else {
1853 // Nothing to do. If there is no varEnv, the enclosing
1854 // file was called by fb_autoload_map, which wants a
1855 // local scope.
1858 tvDecRefGen(
1859 g_context->invokeFunc(unit->getMain(nullptr),
1860 init_null_variant,
1861 nullptr, nullptr, ve)
1863 } else {
1864 Stats::inc(Stats::PseudoMain_SkipDeep);
1867 obj = mi->mergeableObj(++ix);
1868 k = MergeKind(uintptr_t(obj) & 7);
1869 } while (k == MergeKind::ReqDoc);
1870 continue;
1871 case MergeKind::TypeAlias:
1872 do {
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);
1885 continue;
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)) {
1893 return;
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;
1908 if (delta) {
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);
1919 if (newMi != mi) {
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 ||
1930 isMergeOnly());
1932 m_mergeState.fetch_and(~MergeState::NeedsCompact,
1933 std::memory_order_relaxed);
1935 return;
1937 // Normal cases should continue, KindDone returns
1938 not_reached();
1939 } while (true);
1942 ///////////////////////////////////////////////////////////////////////////////
1943 // Info arrays.
1945 namespace {
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()));
1953 } else {
1954 a.append(make_tv<KindOfPersistentString>(c->name()));
1958 return a;
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()));
1970 return a;
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 ///////////////////////////////////////////////////////////////////////////////
1999 // Pretty printer.
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)) {
2029 out.put('\n');
2030 funcIt->second->prettyPrint(out);
2031 ++funcIt;
2032 prevLineNum = -1;
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)
2047 << std::endl;
2048 it += instrLen(it);
2052 std::string Unit::toString() const {
2053 std::ostringstream ss;
2054 prettyPrint(ss);
2055 for (auto& pc : m_preClasses) {
2056 pc->prettyPrint(ss);
2058 for (auto& func : funcs()) {
2059 func->prettyPrint(ss);
2061 return ss.str();
2065 ///////////////////////////////////////////////////////////////////////////////
2066 // Other methods.
2068 bool Unit::compileTimeFatal(const StringData*& msg, int& line) const {
2069 auto entry = getMain(nullptr)->getEntry();
2070 auto pc = entry;
2071 // String <id>; Fatal;
2072 // ^^^^^^
2073 if (decode_op(pc) != Op::String) {
2074 return false;
2076 // String <id>; Fatal;
2077 // ^^^^
2078 Id id = *(Id*)pc;
2079 pc += sizeof(Id);
2080 // String <id>; Fatal;
2081 // ^^^^^
2082 if (decode_op(pc) != Op::Fatal) {
2083 return false;
2085 msg = lookupLitstrId(id);
2086 line = getLineNumber(Offset(pc - entry));
2087 return true;
2090 bool Unit::parseFatal(const StringData*& msg, int& line) const {
2091 if (!compileTimeFatal(msg, line)) {
2092 return false;
2095 auto pc = getMain(nullptr)->getEntry();
2097 // String <id>
2098 decode_op(pc);
2099 pc += sizeof(Id);
2101 // Fatal <kind>
2102 decode_op(pc);
2103 auto kind_char = *pc;
2104 return kind_char == static_cast<uint8_t>(FatalOp::Parse);