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