Add some data annotation to allow for data profiling
[hiphop-php.git] / hphp / runtime / vm / unit.cpp
blobbc5631aa0a3b52c8727363e48f2ab591e7e74749
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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 "hphp/compiler/option.h"
21 #include "hphp/parser/parser.h"
23 #include "hphp/runtime/base/file-repository.h"
24 #include "hphp/runtime/base/rds.h"
25 #include "hphp/runtime/base/stats.h"
26 #include "hphp/runtime/base/strings.h"
28 #include "hphp/runtime/ext/std/ext_std_variable.h"
29 #include "hphp/runtime/vm/blob-helper.h"
30 #include "hphp/runtime/vm/bytecode.h"
31 #include "hphp/runtime/vm/disas.h"
32 #include "hphp/runtime/vm/func-inline.h"
33 #include "hphp/runtime/vm/repo.h"
34 #include "hphp/runtime/vm/treadmill.h"
35 #include "hphp/runtime/vm/unit-util.h"
37 #include "hphp/runtime/vm/jit/mc-generator.h"
38 #include "hphp/runtime/vm/jit/translator-inline.h"
40 #include "hphp/runtime/vm/verifier/check.h"
42 #include "hphp/util/atomic.h"
43 #include "hphp/util/file-util.h"
44 #include "hphp/util/lock.h"
45 #include "hphp/util/read-only-arena.h"
47 #include "folly/Memory.h"
48 #include "folly/ScopeGuard.h"
50 #include <boost/algorithm/string.hpp>
51 #include <sys/mman.h>
52 #include <tbb/concurrent_unordered_map.h>
53 #include <iostream>
54 #include <iomanip>
56 namespace HPHP {
57 ///////////////////////////////////////////////////////////////////////////////
59 TRACE_SET_MOD(hhbc);
61 const StaticString s_stdin("STDIN");
62 const StaticString s_stdout("STDOUT");
63 const StaticString s_stderr("STDERR");
65 /**
66 * Read typed data from an offset relative to a base address
68 template <class T>
69 T& getDataRef(void* base, unsigned offset) {
70 return *(T*)((char*)base + offset);
73 ReadOnlyArena& get_readonly_arena() {
74 static ReadOnlyArena arena(RuntimeOption::EvalHHBCArenaChunkSize);
75 return arena;
78 // Exports for the admin server.
79 size_t hhbc_arena_capacity() {
80 if (!RuntimeOption::RepoAuthoritative) return 0;
81 return get_readonly_arena().capacity();
84 static const unsigned char*
85 allocateBCRegion(const unsigned char* bc, size_t bclen) {
86 if (RuntimeOption::RepoAuthoritative) {
87 // In RepoAuthoritative, we assume we won't ever deallocate units
88 // and that this is read-only, mostly cold data. So we throw it
89 // in a bump-allocator that's mprotect'd to prevent writes.
90 return static_cast<const unsigned char*>(
91 get_readonly_arena().allocate(bc, bclen)
94 auto mem = static_cast<unsigned char*>(malloc(bclen));
95 std::copy(bc, bc + bclen, mem);
96 return mem;
99 Mutex Unit::s_classesMutex;
101 * We hold onto references to elements of this map. If we use a different
102 * map, we must use one that doesnt invalidate references to its elements
103 * (unless they are deleted, which never happens here). Any standard
104 * associative container will meet this requirement.
106 static NamedEntityMap *s_namedDataMap;
108 static NEVER_INLINE
109 NamedEntity* getNamedEntityHelper(const StringData* str) {
110 if (!str->isStatic()) {
111 str = makeStaticString(str);
114 auto res = s_namedDataMap->insert(str, NamedEntity());
115 return &res.first->second;
118 size_t Unit::GetNamedEntityTableSize() {
119 return s_namedDataMap ? s_namedDataMap->size() : 0;
122 NEVER_INLINE
123 static void initializeNamedDataMap() {
124 NamedEntityMap::Config config;
125 config.growthFactor = 1;
126 s_namedDataMap =
127 new NamedEntityMap(RuntimeOption::EvalInitialNamedEntityTableSize,
128 config);
131 NamedEntity* Unit::GetNamedEntity(const StringData* str,
132 bool allowCreate /*= true*/,
133 String* normalizedStr /*= nullptr*/) {
134 if (UNLIKELY(!s_namedDataMap)) {
135 initializeNamedDataMap();
137 NamedEntityMap::iterator it = s_namedDataMap->find(str);
138 if (LIKELY(it != s_namedDataMap->end())) return &it->second;
139 if (needsNSNormalization(str)) {
140 auto normStr = normalizeNS(StrNR(str).asString());
141 if (normalizedStr) {
142 *normalizedStr = normStr;
144 return GetNamedEntity(normStr.get(), allowCreate, normalizedStr);
146 if (LIKELY(allowCreate)) { return getNamedEntityHelper(str); }
147 return nullptr;
150 void NamedEntity::setCachedFunc(Func* f) {
151 *m_cachedFunc = f;
154 Func* NamedEntity::getCachedFunc() const {
155 return LIKELY(m_cachedFunc.bound()) ? *m_cachedFunc : nullptr;
158 void NamedEntity::setCachedClass(Class* f) {
159 *m_cachedClass = f;
162 Class* NamedEntity::getCachedClass() const {
163 return LIKELY(m_cachedClass.bound()) ? *m_cachedClass : nullptr;
166 void NamedEntity::setCachedTypeAlias(const TypeAliasReq& td) {
167 *m_cachedTypeAlias = td;
170 const TypeAliasReq* NamedEntity::getCachedTypeAlias() const {
171 // TODO(#2103214): support persistent typeAliases
172 return m_cachedTypeAlias.bound() && m_cachedTypeAlias->name ?
173 m_cachedTypeAlias.get() : nullptr;
176 void NamedEntity::pushClass(Class* cls) {
177 assert(!cls->m_nextClass);
178 cls->m_nextClass = m_clsList.load(std::memory_order_acquire);
179 m_clsList.store(cls, std::memory_order_release);
182 void NamedEntity::removeClass(Class* goner) {
183 Class* head = m_clsList.load(std::memory_order_acquire);
184 if (!head) return;
185 if (head == goner) {
186 return m_clsList.store(head->m_nextClass, std::memory_order_release);
188 Class** cls = &head->m_nextClass;
189 while (*cls != goner) {
190 assert(*cls);
191 cls = &(*cls)->m_nextClass;
193 *cls = goner->m_nextClass;
196 UnitMergeInfo* UnitMergeInfo::alloc(size_t size) {
197 UnitMergeInfo* mi = (UnitMergeInfo*)malloc(
198 sizeof(UnitMergeInfo) + size * sizeof(void*));
199 mi->m_firstHoistableFunc = 0;
200 mi->m_firstHoistablePreClass = 0;
201 mi->m_firstMergeablePreClass = 0;
202 mi->m_mergeablesSize = size;
203 return mi;
206 Array Unit::getFunctions(bool system) {
207 // Return an array of all defined functions. This method is used
208 // to support get_defined_functions().
209 Array a = Array::Create();
210 if (s_namedDataMap) {
211 for (NamedEntityMap::const_iterator it = s_namedDataMap->begin();
212 it != s_namedDataMap->end(); ++it) {
213 Func* func_ = it->second.getCachedFunc();
214 if (!func_ || (system ^ func_->isBuiltin()) || func_->isGenerated()) {
215 continue;
217 a.append(func_->nameRef());
220 return a;
223 AllClasses::AllClasses()
224 : m_next(s_namedDataMap->begin())
225 , m_end(s_namedDataMap->end())
226 , m_current(m_next != m_end ? m_next->second.clsList() : nullptr) {
227 if (!empty()) skip();
230 void AllClasses::skip() {
231 if (!m_current) {
232 assert(!empty());
233 ++m_next;
234 while (!empty()) {
235 m_current = m_next->second.clsList();
236 if (m_current) break;
237 ++m_next;
240 assert(empty() || front());
243 void AllClasses::next() {
244 m_current = m_current->m_nextClass;
245 skip();
248 bool AllClasses::empty() const {
249 return m_next == m_end;
252 Class* AllClasses::front() const {
253 assert(!empty());
254 assert(m_current);
255 return m_current;
258 Class* AllClasses::popFront() {
259 Class* cls = front();
260 next();
261 return cls;
264 class AllCachedClasses {
265 NamedEntityMap::iterator m_next, m_end;
267 void skip() {
268 Class* cls;
269 while (!empty()) {
270 cls = m_next->second.clsList();
271 if (cls && cls->getCached() &&
272 (cls->parent() != SystemLib::s_ClosureClass)) break;
273 ++m_next;
277 public:
278 AllCachedClasses()
279 : m_next(s_namedDataMap->begin())
280 , m_end(s_namedDataMap->end())
282 skip();
284 bool empty() const {
285 return m_next == m_end;
287 Class* front() {
288 assert(!empty());
289 Class* c = m_next->second.clsList();
290 assert(c);
291 c = c->getCached();
292 assert(c);
293 return c;
295 Class* popFront() {
296 Class* c = front();
297 ++m_next;
298 skip();
299 return c;
303 Array Unit::getClassesWithAttrInfo(Attr attrs, bool inverse) {
304 Array a = Array::Create();
305 if (s_namedDataMap) {
306 for (AllCachedClasses ac; !ac.empty();) {
307 Class* c = ac.popFront();
308 if ((c->attrs() & attrs) ? !inverse : inverse) {
309 if (c->isBuiltin()) {
310 a.prepend(c->nameRef());
311 } else {
312 a.append(c->nameRef());
317 return a;
320 Array Unit::getClassesInfo() {
321 // Return an array of all defined class names. This method is used to
322 // support get_declared_classes().
323 return getClassesWithAttrInfo(AttrInterface | AttrTrait,
324 /* inverse = */ true);
327 Array Unit::getInterfacesInfo() {
328 // Return an array of all defined interface names. This method is used to
329 // support get_declared_interfaces().
330 return getClassesWithAttrInfo(AttrInterface);
333 Array Unit::getTraitsInfo() {
334 // Returns an array with all defined trait names. This method is used to
335 // support get_declared_traits().
336 return getClassesWithAttrInfo(AttrTrait);
339 bool Unit::checkStringId(Id id) const {
340 if (isGlobalLitstrId(id)) {
341 return decodeGlobalLitstrId(id) < LitstrTable::get().numLitstrs();
343 return id >= 0 && unsigned(id) < numLitstrs();
346 bool Unit::MetaHandle::findMeta(const Unit* unit, Offset offset) {
347 if (!unit->m_bc_meta_len) return false;
348 assert(unit->m_bc_meta);
349 Offset* index1 = (Offset*)unit->m_bc_meta;
350 Offset* index2 = index1 + *index1 + 1;
352 assert(index1[*index1 + 1] == INT_MAX); // sentinel
353 assert(offset >= 0 && (unsigned)offset < unit->m_bclen);
354 assert(cur == 0 || index == index1);
355 if (cur && offset >= index[cur]) {
356 while (offset >= index[cur+1]) cur++;
357 } else {
358 int hi = *index1 + 2;
359 int lo = 1;
360 while (hi - lo > 1) {
361 int mid = (hi + lo) >> 1;
362 if (offset >= index1[mid]) {
363 lo = mid;
364 } else {
365 hi = mid;
368 index = index1;
369 cur = lo;
371 assert(cur <= (unsigned)*index1);
372 assert((unsigned)index2[cur] <= unit->m_bc_meta_len);
373 ptr = unit->m_bc_meta + index2[cur];
374 return index[cur] == offset;
377 bool Unit::MetaHandle::nextArg(MetaInfo& info) {
378 assert(index && cur && ptr);
379 uint8_t* end = (uint8_t*)index + index[*index + cur + 2];
380 assert(ptr <= end);
381 if (ptr == end) return false;
382 info.m_kind = (Unit::MetaInfo::Kind)*ptr++;
383 info.m_arg = *ptr++;
384 info.m_data = decodeVariableSizeImm(&ptr);
385 return true;
388 //=============================================================================
389 // LitstrTable.
391 LitstrTable* LitstrTable::s_litstrTable = nullptr;
393 Id LitstrTable::mergeLitstr(const StringData* litstr) {
394 m_mutex.lock();
395 assert(!m_safeToRead);
396 auto it = m_litstr2id.find(litstr);
397 if (it == m_litstr2id.end()) {
398 const StringData* str = makeStaticString(litstr);
399 Id id = m_litstrs.size();
400 NamedEntityPair np = { str, nullptr };
401 m_litstr2id[str] = id;
402 m_litstrs.push_back(str);
403 m_namedInfo.push_back(np);
404 m_mutex.unlock();
405 return id;
406 } else {
407 m_mutex.unlock();
408 return it->second;
412 void LitstrTable::insert(RepoTxn& txn, UnitOrigin uo) {
413 Repo& repo = Repo::get();
414 LitstrRepoProxy& lsrp = repo.lsrp();
415 int repoId = Repo::get().repoIdForNewUnit(uo);
416 for (int i = 0; i < m_litstrs.size(); ++i) {
417 lsrp.insertLitstr(repoId).insert(txn, i, m_litstrs[i]);
421 //=============================================================================
422 // Unit.
424 Unit::Unit() {
425 tvWriteUninit(&m_mainReturn);
428 Unit::~Unit() {
429 if (!RuntimeOption::RepoAuthoritative) {
430 if (debug) {
431 // poison released bytecode
432 memset(const_cast<unsigned char*>(m_bc), 0xff, m_bclen);
434 free(const_cast<unsigned char*>(m_bc));
435 free(const_cast<unsigned char*>(m_bc_meta));
438 if (m_mergeInfo) {
439 for (auto* func : mutableFuncs()) Func::destroy(func);
442 // ExecutionContext and the TC may retain references to Class'es, so
443 // it is possible for Class'es to outlive their Unit.
444 for (int i = m_preClasses.size(); i--; ) {
445 PreClass* pcls = m_preClasses[i].get();
446 Class* cls = pcls->namedEntity()->clsList();
447 while (cls) {
448 Class* cur = cls;
449 cls = cls->m_nextClass;
450 if (cur->preClass() == pcls) {
451 cur->destroy();
456 free(m_mergeInfo);
458 if (m_pseudoMainCache) {
459 for (auto it = m_pseudoMainCache->begin();
460 it != m_pseudoMainCache->end(); ++it) {
461 delete it->second;
463 delete m_pseudoMainCache;
467 void* Unit::operator new(size_t sz) {
468 return low_malloc(sz);
471 void Unit::operator delete(void* p, size_t sz) {
472 low_free(p);
475 bool Unit::compileTimeFatal(const StringData*& msg, int& line) const {
476 // A compile-time fatal is encoded as a pseudomain that contains precisely:
478 // String <id>; Fatal;
480 // Decode enough of pseudomain to determine whether it contains a
481 // compile-time fatal, and if so, extract the error message and line number.
482 auto entry = reinterpret_cast<const Op*>(getMain()->getEntry());
483 auto pc = entry;
484 // String <id>; Fatal;
485 // ^^^^^^
486 if (*pc != Op::String) {
487 return false;
489 pc++;
490 // String <id>; Fatal;
491 // ^^^^
492 Id id = *(Id*)pc;
493 pc += sizeof(Id);
494 // String <id>; Fatal;
495 // ^^^^^
496 if (*pc != Op::Fatal) {
497 return false;
499 msg = lookupLitstrId(id);
500 line = getLineNumber(Offset(pc - entry));
501 return true;
504 bool Unit::parseFatal(const StringData*& msg, int& line) const {
505 if (!compileTimeFatal(msg, line)) {
506 return false;
509 auto pc = getMain()->getEntry();
511 // two opcodes + String's ID
512 pc += sizeof(Id) + 2;
514 auto kind_char = *pc;
515 return kind_char == static_cast<uint8_t>(FatalOp::Parse);
518 class FrameRestore {
519 public:
520 explicit FrameRestore(const PreClass* preClass) {
521 auto const ec = g_context.getNoCheck();
522 ActRec* fp = ec->getFP();
523 PC pc = ec->getPC();
525 if (ec->m_stack.top() &&
526 (!fp || fp->m_func->unit() != preClass->unit())) {
527 m_top = ec->m_stack.top();
528 m_fp = fp;
529 m_pc = pc;
532 we can be called from Unit::merge, which hasnt yet setup
533 the frame (because often it doesnt need to).
534 Set up a fake frame here, in case of errors.
535 But note that mergeUnit is called for systemlib etc before the
536 stack has been setup. So dont do anything if m_stack.top()
537 is NULL
539 ActRec &tmp = *ec->m_stack.allocA();
540 tmp.m_savedRbp = (uint64_t)fp;
541 tmp.m_savedRip = 0;
542 tmp.m_func = preClass->unit()->getMain();
543 tmp.m_soff = !fp
545 : fp->m_func->unit()->offsetOf(pc) - fp->m_func->base();
546 tmp.setThis(nullptr);
547 tmp.m_varEnv = 0;
548 tmp.initNumArgs(0);
549 ec->m_fp = &tmp;
550 ec->m_pc = preClass->unit()->at(preClass->getOffset());
551 ec->pushLocalsAndIterators(tmp.m_func);
552 } else {
553 m_top = nullptr;
554 m_fp = nullptr;
555 m_pc = nullptr;
558 ~FrameRestore() {
559 auto const ec = g_context.getNoCheck();
560 if (m_top) {
561 ec->m_stack.top() = m_top;
562 ec->m_fp = m_fp;
563 ec->m_pc = m_pc;
566 private:
567 Cell* m_top;
568 ActRec* m_fp;
569 PC m_pc;
572 Class* Unit::defClass(const PreClass* preClass,
573 bool failIsFatal /* = true */) {
574 NamedEntity* const nameList = preClass->namedEntity();
575 Class* top = nameList->clsList();
578 * Check if there is already a name defined in this request for this
579 * NamedEntity.
581 * Raise a fatal unless the existing class definition is identical to the
582 * one this invocation would create.
584 if (auto current = nameList->getCachedTypeAlias()) {
585 FrameRestore fr(preClass);
586 raise_error("Cannot declare class with the same name (%s) as an "
587 "existing type", current->name->data());
588 return nullptr;
591 // If there was already a class declared with DefClass, check if
592 // it's compatible.
593 if (Class* cls = nameList->getCachedClass()) {
594 if (cls->preClass() != preClass) {
595 if (failIsFatal) {
596 FrameRestore fr(preClass);
597 raise_error("Class already declared: %s", preClass->name()->data());
599 return nullptr;
601 return cls;
604 // Get a compatible Class, and add it to the list of defined classes.
605 Class* parent = nullptr;
606 for (;;) {
607 // Search for a compatible extant class. Searching from most to least
608 // recently created may have better locality than alternative search orders.
609 // In addition, its the only simple way to make this work lock free...
610 for (Class* class_ = top; class_ != nullptr; ) {
611 Class* cur = class_;
612 class_ = class_->m_nextClass;
613 if (cur->preClass() != preClass) continue;
614 Class::Avail avail = cur->avail(parent, failIsFatal /*tryAutoload*/);
615 if (LIKELY(avail == Class::Avail::True)) {
616 cur->setCached();
617 DEBUGGER_ATTACHED_ONLY(phpDebuggerDefClassHook(cur));
618 return cur;
620 if (avail == Class::Avail::Fail) {
621 if (failIsFatal) {
622 FrameRestore fr(preClass);
623 raise_error("unknown class %s", parent->name()->data());
625 return nullptr;
627 assert(avail == Class::Avail::False);
630 // Create a new class.
631 if (!parent && preClass->parent()->size() != 0) {
632 parent = Unit::getClass(preClass->parent(), failIsFatal);
633 if (parent == nullptr) {
634 if (failIsFatal) {
635 FrameRestore fr(preClass);
636 raise_error("unknown class %s", preClass->parent()->data());
638 return nullptr;
642 ClassPtr newClass;
644 FrameRestore fr(preClass);
645 newClass = Class::newClass(const_cast<PreClass*>(preClass), parent);
647 Lock l(Unit::s_classesMutex);
649 if (UNLIKELY(top != nameList->clsList())) {
650 top = nameList->clsList();
651 continue;
654 bool const isPersistent =
655 (!SystemLib::s_inited || RuntimeOption::RepoAuthoritative) &&
656 newClass->verifyPersistent();
657 nameList->m_cachedClass.bind(
658 isPersistent ? RDS::Mode::Persistent
659 : RDS::Mode::Normal
661 newClass->setClassHandle(nameList->m_cachedClass);
662 newClass.get()->incAtomicCount();
664 if (InstanceBits::initFlag.load(std::memory_order_acquire)) {
665 // If the instance bitmap has already been set up, we can just
666 // initialize our new class's bits and add ourselves to the class
667 // list normally.
668 newClass->setInstanceBits();
669 nameList->pushClass(newClass.get());
670 } else {
671 // Otherwise, we have to grab the read lock. If the map has been
672 // initialized since we checked, initialize the bits normally. If not,
673 // we must add the new class to the class list before dropping the lock
674 // to ensure its bits are initialized when the time comes.
675 ReadLock l(InstanceBits::lock);
676 if (InstanceBits::initFlag.load(std::memory_order_acquire)) {
677 newClass->setInstanceBits();
679 nameList->pushClass(newClass.get());
681 if (RuntimeOption::EvalPerfDataMap) {
682 Debug::DebugInfo::recordDataMap(
683 newClass.get(), newClass.get() + 1,
684 folly::format("Class-{}", preClass->name()->data()).str());
685 Debug::DebugInfo::recordDataMap(
686 (char*)(intptr_t)nameList->m_cachedClass.handle(),
687 (char*)(intptr_t)nameList->m_cachedClass.handle() + sizeof(void*),
688 folly::format("rds+Class-{}", preClass->name()->data()).str());
691 * call setCached after adding to the class list, otherwise the
692 * target-cache short circuit at the top could return a class
693 * which is not yet on the clsList().
695 newClass.get()->setCached();
696 DEBUGGER_ATTACHED_ONLY(phpDebuggerDefClassHook(newClass.get()));
697 return newClass.get();
701 bool Unit::aliasClass(Class* original, const StringData* alias) {
702 auto const aliasNe = Unit::GetNamedEntity(alias);
703 aliasNe->m_cachedClass.bind();
705 auto const aliasClass = aliasNe->getCachedClass();
706 if (aliasClass) {
707 raise_warning("Cannot redeclare class %s", alias->data());
708 return false;
710 aliasNe->setCachedClass(original);
711 return true;
714 void Unit::defTypeAlias(Id id) {
715 assert(id < m_typeAliases.size());
716 auto thisType = &m_typeAliases[id];
717 auto nameList = GetNamedEntity(thisType->name);
718 const StringData* typeName = thisType->value;
721 * Check if this name already was defined as a type alias, and if so
722 * make sure it is compatible.
724 if (auto current = nameList->getCachedTypeAlias()) {
725 if (thisType->kind != current->kind ||
726 thisType->nullable != current->nullable ||
727 Unit::lookupClass(typeName) != current->klass) {
728 raise_error("The type %s is already defined to an incompatible type",
729 thisType->name->data());
731 return;
734 // There might also be a class with this name already.
735 if (nameList->getCachedClass()) {
736 raise_error("The name %s is already defined as a class",
737 thisType->name->data());
738 return;
741 // TODO(#2103214): persistent type alias support
742 if (!nameList->m_cachedTypeAlias.bound()) {
743 nameList->m_cachedTypeAlias.bind();
744 RDS::recordRds(nameList->m_cachedTypeAlias.handle(),
745 sizeof(TypeAliasReq),
746 "TypeAlias", typeName->data());
749 * If this type alias is a KindOfObject and the name on the right
750 * hand side was another type alias, we will bind the name to the
751 * other side for this request (i.e. resolve that type alias now).
753 * We need to inspect the right hand side and figure out what it was
754 * first.
756 * If the right hand side was a class, we need to autoload and
757 * ensure it exists at this point.
760 if (thisType->kind != KindOfObject) {
761 nameList->setCachedTypeAlias(
762 TypeAliasReq { thisType->kind,
763 thisType->nullable,
764 nullptr,
765 thisType->name }
767 return;
770 auto targetNameList = GetNamedEntity(typeName);
771 if (auto targetTd = getTypeAliasWithAutoload(targetNameList, typeName)) {
772 nameList->setCachedTypeAlias(
773 TypeAliasReq { targetTd->kind,
774 thisType->nullable || targetTd->nullable,
775 targetTd->klass,
776 thisType->name }
778 return;
780 if (auto klass = Unit::loadClass(typeName)) {
781 nameList->setCachedTypeAlias(
782 TypeAliasReq { KindOfObject,
783 thisType->nullable,
784 klass,
785 thisType->name }
787 return;
790 raise_error("Unknown type or class %s", typeName->data());
793 void Unit::renameFunc(const StringData* oldName, const StringData* newName) {
794 // renameFunc() should only be used by ExecutionContext::createFunction.
795 // We do a linear scan over all the functions in the unit searching for the
796 // func with a given name; in practice this is okay because the units created
797 // by create_function() will always have the function being renamed at the
798 // beginning
799 assert(oldName && oldName->isStatic());
800 assert(newName && newName->isStatic());
802 for (MutableFuncRange fr(hoistableFuncs()); !fr.empty(); ) {
803 Func* func = fr.popFront();
804 const StringData* name = func->name();
805 assert(name);
806 if (name->same(oldName)) {
807 func->rename(newName);
808 break;
813 Class* Unit::loadClass(const NamedEntity* ne,
814 const StringData* name) {
815 Class* cls;
816 if (LIKELY((cls = ne->getCachedClass()) != nullptr)) {
817 return cls;
819 return loadMissingClass(ne, name);
822 Class* Unit::loadMissingClass(const NamedEntity* ne,
823 const StringData* name) {
824 JIT::VMRegAnchor _;
825 AutoloadHandler::s_instance->invokeHandler(
826 StrNR(const_cast<StringData*>(name)));
827 return Unit::lookupClass(ne);
830 Class* Unit::getClass(const NamedEntity* ne,
831 const StringData *name, bool tryAutoload) {
832 Class *cls = lookupClass(ne);
833 if (UNLIKELY(!cls)) {
834 if (tryAutoload) {
835 return loadMissingClass(ne, name);
838 return cls;
841 bool Unit::classExists(const StringData* name, bool autoload, Attr typeAttrs) {
842 Class* cls = Unit::getClass(name, autoload);
843 return cls && (cls->attrs() & (AttrInterface | AttrTrait)) == typeAttrs;
846 void Unit::loadFunc(const Func *func) {
847 assert(!func->isMethod());
848 auto const ne = func->getNamedEntity();
849 auto const isPersistent =
850 (RuntimeOption::RepoAuthoritative || !SystemLib::s_inited) &&
851 (func->attrs() & AttrPersistent);
852 ne->m_cachedFunc.bind(
853 isPersistent ? RDS::Mode::Persistent
854 : RDS::Mode::Normal
856 const_cast<Func*>(func)->setFuncHandle(ne->m_cachedFunc);
857 if (RuntimeOption::EvalPerfDataMap) {
858 Debug::DebugInfo::recordDataMap(
859 (char*)(intptr_t)ne->m_cachedFunc.handle(),
860 (char*)(intptr_t)ne->m_cachedFunc.handle() + sizeof(void*),
861 folly::format("rds+Func-{}", func->name()->data()).str());
865 static void mergeCns(TypedValue& tv, TypedValue *value,
866 StringData *name) {
867 if (LIKELY(tv.m_type == KindOfUninit)) {
868 tv = *value;
869 return;
872 raise_notice(Strings::CONSTANT_ALREADY_DEFINED, name->data());
875 static SimpleMutex unitInitLock(false /* reentrant */, RankUnitInit);
877 void Unit::initialMerge() {
878 unitInitLock.assertOwnedBySelf();
879 if (LIKELY(m_mergeState == UnitMergeStateUnmerged)) {
880 if (RuntimeOption::EvalPerfDataMap) {
881 Debug::DebugInfo::recordDataMap(
882 this, this + 1, folly::format("Unit-{}", m_filepath->data()).str());
884 int state = 0;
885 bool needsCompact = false;
886 m_mergeState = UnitMergeStateMerging;
888 bool allFuncsUnique = RuntimeOption::RepoAuthoritative;
889 for (MutableFuncRange fr(nonMainFuncs()); !fr.empty();) {
890 Func* f = fr.popFront();
891 if (allFuncsUnique) {
892 allFuncsUnique = (f->attrs() & AttrUnique);
894 loadFunc(f);
895 if (RDS::isPersistentHandle(f->funcHandle())) {
896 needsCompact = true;
899 if (allFuncsUnique) state |= UnitMergeStateUniqueFuncs;
900 if (RuntimeOption::RepoAuthoritative || !SystemLib::s_inited) {
902 * The mergeables array begins with the hoistable Func*s,
903 * followed by the (potentially) hoistable Class*s.
905 * If the Unit is merge only, it then contains enough information
906 * to simulate executing the pseudomain. Normally, this is just
907 * the Class*s that might not be hoistable. In RepoAuthoritative
908 * mode it also includes assignments of the form:
909 * $GLOBALS[string-literal] = scalar;
910 * defines of the form:
911 * define(string-literal, scalar);
912 * and requires.
914 * These cases are differentiated using the bottom 3 bits
915 * of the pointer. In the case of a define or a global,
916 * the pointer will be followed by a TypedValue representing
917 * the value being defined/assigned.
919 int ix = m_mergeInfo->m_firstHoistablePreClass;
920 int end = m_mergeInfo->m_firstMergeablePreClass;
921 while (ix < end) {
922 PreClass* pre = (PreClass*)m_mergeInfo->mergeableObj(ix++);
923 if (pre->attrs() & AttrUnique) {
924 needsCompact = true;
927 if (isMergeOnly()) {
928 ix = m_mergeInfo->m_firstMergeablePreClass;
929 end = m_mergeInfo->m_mergeablesSize;
930 while (ix < end) {
931 void *obj = m_mergeInfo->mergeableObj(ix);
932 UnitMergeKind k = UnitMergeKind(uintptr_t(obj) & 7);
933 switch (k) {
934 case UnitMergeKindUniqueDefinedClass:
935 case UnitMergeKindDone:
936 not_reached();
937 case UnitMergeKindClass:
938 if (((PreClass*)obj)->attrs() & AttrUnique) {
939 needsCompact = true;
941 break;
942 case UnitMergeKindReqDoc: {
943 StringData* s = (StringData*)((char*)obj - (int)k);
944 auto const efile = g_context->lookupIncludeRoot(s,
945 InclOpFlags::DocRoot, nullptr, this);
946 assert(efile);
947 Unit* unit = efile->unit();
948 unit->initialMerge();
949 m_mergeInfo->mergeableObj(ix) = (void*)((char*)unit + (int)k);
950 break;
952 case UnitMergeKindPersistentDefine:
953 needsCompact = true;
954 case UnitMergeKindDefine: {
955 StringData* s = (StringData*)((char*)obj - (int)k);
956 auto* v = (TypedValueAux*) m_mergeInfo->mergeableData(ix + 1);
957 ix += sizeof(*v) / sizeof(void*);
958 v->rdsHandle() = makeCnsHandle(
959 s, k == UnitMergeKindPersistentDefine);
960 if (k == UnitMergeKindPersistentDefine) {
961 mergeCns(RDS::handleToRef<TypedValue>(v->rdsHandle()),
962 v, s);
964 break;
966 case UnitMergeKindGlobal:
967 // Skip over the value of the global, embedded in mergeableData
968 ix += sizeof(TypedValueAux) / sizeof(void*);
969 break;
971 ix++;
974 if (needsCompact) state |= UnitMergeStateNeedsCompact;
976 m_mergeState = UnitMergeStateMerged | state;
980 const Cell* Unit::lookupCns(const StringData* cnsName) {
981 auto const handle = lookupCnsHandle(cnsName);
982 if (LIKELY(handle != 0)) {
983 TypedValue& tv = RDS::handleToRef<TypedValue>(handle);
984 if (LIKELY(tv.m_type != KindOfUninit)) {
985 assert(cellIsPlausible(tv));
986 return &tv;
988 if (UNLIKELY(tv.m_data.pref != nullptr)) {
989 ClassInfo::ConstantInfo* ci =
990 (ClassInfo::ConstantInfo*)(void*)tv.m_data.pref;
991 auto const tvRet = const_cast<Variant&>(
992 ci->getDeferredValue()).asTypedValue();
993 assert(cellIsPlausible(*tvRet));
994 if (LIKELY(tvRet->m_type != KindOfUninit)) {
995 return tvRet;
999 if (UNLIKELY(RDS::s_constants().get() != nullptr)) {
1000 return RDS::s_constants()->nvGet(cnsName);
1002 return nullptr;
1005 const Cell* Unit::lookupPersistentCns(const StringData* cnsName) {
1006 auto const handle = lookupCnsHandle(cnsName);
1007 if (!RDS::isPersistentHandle(handle)) return nullptr;
1008 auto const ret = &RDS::handleToRef<TypedValue>(handle);
1009 assert(cellIsPlausible(*ret));
1010 return ret;
1013 const TypedValue* Unit::loadCns(const StringData* cnsName) {
1014 auto const tv = lookupCns(cnsName);
1015 if (LIKELY(tv != nullptr)) return tv;
1017 if (needsNSNormalization(cnsName)) {
1018 return loadCns(normalizeNS(cnsName));
1021 if (!AutoloadHandler::s_instance->autoloadConstant(
1022 const_cast<StringData*>(cnsName))) {
1023 return nullptr;
1025 return lookupCns(cnsName);
1028 bool Unit::defCns(const StringData* cnsName, const TypedValue* value,
1029 bool persistent /* = false */) {
1030 auto const handle = makeCnsHandle(cnsName, persistent);
1032 if (UNLIKELY(handle == 0)) {
1033 if (UNLIKELY(!RDS::s_constants().get())) {
1035 * This only happens when we call define on a non
1036 * static string. Not worth presizing or otherwise
1037 * optimizing for.
1039 RDS::s_constants() =
1040 Array::attach(MixedArray::MakeReserve(1));
1042 auto const existed = !!RDS::s_constants()->nvGet(cnsName);
1043 if (!existed) {
1044 RDS::s_constants().set(StrNR(cnsName),
1045 tvAsCVarRef(value), true /* isKey */);
1046 return true;
1048 raise_notice(Strings::CONSTANT_ALREADY_DEFINED, cnsName->data());
1049 return false;
1051 return defCnsHelper(handle, value, cnsName);
1054 uint64_t Unit::defCnsHelper(uint64_t ch,
1055 const TypedValue *value,
1056 const StringData *cnsName) {
1057 TypedValue* cns = &RDS::handleToRef<TypedValue>(ch);
1058 if (UNLIKELY(cns->m_type != KindOfUninit) ||
1059 UNLIKELY(cns->m_data.pref != nullptr)) {
1060 raise_notice(Strings::CONSTANT_ALREADY_DEFINED, cnsName->data());
1061 } else if (UNLIKELY(!tvAsCVarRef(value).isAllowedAsConstantValue())) {
1062 raise_warning(Strings::CONSTANTS_MUST_BE_SCALAR);
1063 } else {
1064 Variant v = tvAsCVarRef(value);
1065 v.setEvalScalar();
1066 cns->m_data = v.asTypedValue()->m_data;
1067 cns->m_type = v.asTypedValue()->m_type;
1068 return true;
1070 return false;
1073 void Unit::defDynamicSystemConstant(const StringData* cnsName,
1074 const void* data) {
1075 static const bool kServer = RuntimeOption::ServerExecutionMode();
1076 // Zend doesn't define the STD* streams in server mode so we don't either
1077 if (UNLIKELY(kServer &&
1078 (s_stdin.equal(cnsName) ||
1079 s_stdout.equal(cnsName) ||
1080 s_stderr.equal(cnsName)))) {
1081 return;
1083 auto const handle = makeCnsHandle(cnsName, true);
1084 assert(handle);
1085 TypedValue* cns = &RDS::handleToRef<TypedValue>(handle);
1086 assert(cns->m_type == KindOfUninit);
1087 cns->m_data.pref = (RefData*)data;
1090 static void setGlobal(StringData* name, TypedValue *value) {
1091 g_context->m_globalVarEnv->set(name, value);
1094 void Unit::merge() {
1095 if (UNLIKELY(!(m_mergeState & UnitMergeStateMerged))) {
1096 SimpleLock lock(unitInitLock);
1097 initialMerge();
1100 if (UNLIKELY(isDebuggerAttached())) {
1101 mergeImpl<true>(RDS::tl_base, m_mergeInfo);
1102 } else {
1103 mergeImpl<false>(RDS::tl_base, m_mergeInfo);
1107 void* Unit::replaceUnit() const {
1108 if (m_mergeState & UnitMergeStateEmpty) return nullptr;
1109 if (isMergeOnly() &&
1110 m_mergeInfo->m_mergeablesSize == m_mergeInfo->m_firstHoistableFunc + 1) {
1111 void* obj =
1112 m_mergeInfo->mergeableObj(m_mergeInfo->m_firstHoistableFunc);
1113 if (m_mergeInfo->m_firstMergeablePreClass ==
1114 m_mergeInfo->m_firstHoistableFunc) {
1115 int k = uintptr_t(obj) & 7;
1116 if (k != UnitMergeKindClass) return obj;
1117 } else if (m_mergeInfo->m_firstHoistablePreClass ==
1118 m_mergeInfo->m_firstHoistableFunc) {
1119 if (uintptr_t(obj) & 1) {
1120 return (char*)obj - 1 + (int)UnitMergeKindUniqueDefinedClass;
1124 return const_cast<Unit*>(this);
1127 size_t compactUnitMergeInfo(UnitMergeInfo* in, UnitMergeInfo* out) {
1128 Func** it = in->funcHoistableBegin();
1129 Func** fend = in->funcEnd();
1130 Func** iout = 0;
1131 unsigned ix, end, oix = 0;
1133 if (out) {
1134 if (in != out) memcpy(out, in, uintptr_t(it) - uintptr_t(in));
1135 iout = out->funcHoistableBegin();
1138 size_t delta = 0;
1139 while (it != fend) {
1140 Func* func = *it++;
1141 if (RDS::isPersistentHandle(func->funcHandle())) {
1142 delta++;
1143 } else if (iout) {
1144 *iout++ = func;
1148 if (out) {
1149 oix = out->m_firstHoistablePreClass -= delta;
1152 ix = in->m_firstHoistablePreClass;
1153 end = in->m_firstMergeablePreClass;
1154 for (; ix < end; ++ix) {
1155 void* obj = in->mergeableObj(ix);
1156 assert((uintptr_t(obj) & 1) == 0);
1157 PreClass* pre = (PreClass*)obj;
1158 if (pre->attrs() & AttrUnique) {
1159 Class* cls = pre->namedEntity()->clsList();
1160 assert(cls && !cls->m_nextClass);
1161 assert(cls->preClass() == pre);
1162 if (RDS::isPersistentHandle(cls->classHandle())) {
1163 delta++;
1164 } else if (out) {
1165 out->mergeableObj(oix++) = (void*)(uintptr_t(cls) | 1);
1167 } else if (out) {
1168 out->mergeableObj(oix++) = obj;
1172 if (out) {
1173 out->m_firstMergeablePreClass = oix;
1176 end = in->m_mergeablesSize;
1177 while (ix < end) {
1178 void* obj = in->mergeableObj(ix++);
1179 UnitMergeKind k = UnitMergeKind(uintptr_t(obj) & 7);
1180 switch (k) {
1181 case UnitMergeKindClass: {
1182 PreClass* pre = (PreClass*)obj;
1183 if (pre->attrs() & AttrUnique) {
1184 Class* cls = pre->namedEntity()->clsList();
1185 assert(cls && !cls->m_nextClass);
1186 assert(cls->preClass() == pre);
1187 if (RDS::isPersistentHandle(cls->classHandle())) {
1188 delta++;
1189 } else if (out) {
1190 out->mergeableObj(oix++) =
1191 (void*)(uintptr_t(cls) | UnitMergeKindUniqueDefinedClass);
1193 } else if (out) {
1194 out->mergeableObj(oix++) = obj;
1196 break;
1198 case UnitMergeKindUniqueDefinedClass:
1199 not_reached();
1201 case UnitMergeKindPersistentDefine:
1202 delta += 1 + sizeof(TypedValueAux) / sizeof(void*);
1203 ix += sizeof(TypedValueAux) / sizeof(void*);
1204 break;
1206 case UnitMergeKindDefine:
1207 case UnitMergeKindGlobal:
1208 if (out) {
1209 out->mergeableObj(oix++) = obj;
1210 *(TypedValueAux*)out->mergeableData(oix) =
1211 *(TypedValueAux*)in->mergeableData(ix);
1212 oix += sizeof(TypedValueAux) / sizeof(void*);
1214 ix += sizeof(TypedValueAux) / sizeof(void*);
1215 break;
1217 case UnitMergeKindReqDoc: {
1218 Unit *unit = (Unit*)((char*)obj - (int)k);
1219 void *rep = unit->replaceUnit();
1220 if (!rep) {
1221 delta++;
1222 } else if (out) {
1223 if (rep == unit) {
1224 out->mergeableObj(oix++) = obj;
1225 } else {
1226 out->mergeableObj(oix++) = rep;
1229 break;
1231 case UnitMergeKindDone:
1232 not_reached();
1235 if (out) {
1236 // copy the UnitMergeKindDone marker
1237 out->mergeableObj(oix) = in->mergeableObj(ix);
1238 out->m_mergeablesSize = oix;
1240 return delta;
1243 template <bool debugger>
1244 void Unit::mergeImpl(void* tcbase, UnitMergeInfo* mi) {
1245 assert(m_mergeState & UnitMergeStateMerged);
1247 Func** it = mi->funcHoistableBegin();
1248 Func** fend = mi->funcEnd();
1249 if (it != fend) {
1250 if (LIKELY((m_mergeState & UnitMergeStateUniqueFuncs) != 0)) {
1251 do {
1252 Func* func = *it;
1253 assert(func->top());
1254 getDataRef<Func*>(tcbase, func->funcHandle()) = func;
1255 if (debugger) phpDebuggerDefFuncHook(func);
1256 } while (++it != fend);
1257 } else {
1258 do {
1259 Func* func = *it;
1260 assert(func->top());
1261 setCachedFunc(func, debugger);
1262 } while (++it != fend);
1266 bool redoHoistable = false;
1267 int ix = mi->m_firstHoistablePreClass;
1268 int end = mi->m_firstMergeablePreClass;
1269 // iterate over all the potentially hoistable classes
1270 // with no fatals on failure
1271 if (ix < end) {
1272 do {
1273 // The first time this unit is merged, if the classes turn out to be all
1274 // unique and defined, we replace the PreClass*'s with the corresponding
1275 // Class*'s, with the low-order bit marked.
1276 PreClass* pre = (PreClass*)mi->mergeableObj(ix);
1277 if (LIKELY(uintptr_t(pre) & 1)) {
1278 Stats::inc(Stats::UnitMerge_hoistable);
1279 Class* cls = (Class*)(uintptr_t(pre) & ~1);
1280 if (cls->isPersistent()) {
1281 Stats::inc(Stats::UnitMerge_hoistable_persistent);
1283 if (Stats::enabled() &&
1284 RDS::isPersistentHandle(cls->classHandle())) {
1285 Stats::inc(Stats::UnitMerge_hoistable_persistent_cache);
1287 if (Class* parent = cls->parent()) {
1288 if (parent->isPersistent()) {
1289 Stats::inc(Stats::UnitMerge_hoistable_persistent_parent);
1291 if (Stats::enabled() &&
1292 RDS::isPersistentHandle(parent->classHandle())) {
1293 Stats::inc(Stats::UnitMerge_hoistable_persistent_parent_cache);
1295 if (UNLIKELY(!getDataRef<Class*>(tcbase, parent->classHandle()))) {
1296 redoHoistable = true;
1297 continue;
1300 getDataRef<Class*>(tcbase, cls->classHandle()) = cls;
1301 if (debugger) phpDebuggerDefClassHook(cls);
1302 } else {
1303 if (UNLIKELY(!defClass(pre, false))) {
1304 redoHoistable = true;
1307 } while (++ix < end);
1308 if (UNLIKELY(redoHoistable)) {
1309 // if this unit isnt mergeOnly, we're done
1310 if (!isMergeOnly()) return;
1311 // as a special case, if all the classes are potentially
1312 // hoistable, we dont list them twice, but instead
1313 // iterate over them again
1314 // At first glance, it may seem like we could leave
1315 // the maybe-hoistable classes out of the second list
1316 // and then always reset ix to 0; but that gets this
1317 // case wrong if there's an autoloader for C, and C
1318 // extends B:
1320 // class A {}
1321 // class B implements I {}
1322 // class D extends C {}
1324 // because now A and D go on the maybe-hoistable list
1325 // B goes on the never hoistable list, and we
1326 // fatal trying to instantiate D before B
1327 Stats::inc(Stats::UnitMerge_redo_hoistable);
1328 if (end == (int)mi->m_mergeablesSize) {
1329 ix = mi->m_firstHoistablePreClass;
1330 do {
1331 void* obj = mi->mergeableObj(ix);
1332 if (UNLIKELY(uintptr_t(obj) & 1)) {
1333 Class* cls = (Class*)(uintptr_t(obj) & ~1);
1334 defClass(cls->preClass(), true);
1335 } else {
1336 defClass((PreClass*)obj, true);
1338 } while (++ix < end);
1339 return;
1344 // iterate over all but the guaranteed hoistable classes
1345 // fataling if we fail.
1346 void* obj = mi->mergeableObj(ix);
1347 UnitMergeKind k = UnitMergeKind(uintptr_t(obj) & 7);
1348 do {
1349 switch(k) {
1350 case UnitMergeKindClass:
1351 do {
1352 Stats::inc(Stats::UnitMerge_mergeable);
1353 Stats::inc(Stats::UnitMerge_mergeable_class);
1354 defClass((PreClass*)obj, true);
1355 obj = mi->mergeableObj(++ix);
1356 k = UnitMergeKind(uintptr_t(obj) & 7);
1357 } while (!k);
1358 continue;
1360 case UnitMergeKindUniqueDefinedClass:
1361 do {
1362 Stats::inc(Stats::UnitMerge_mergeable);
1363 Stats::inc(Stats::UnitMerge_mergeable_unique);
1364 Class* other = nullptr;
1365 Class* cls = (Class*)((char*)obj - (int)k);
1366 if (cls->isPersistent()) {
1367 Stats::inc(Stats::UnitMerge_mergeable_unique_persistent);
1369 if (Stats::enabled() &&
1370 RDS::isPersistentHandle(cls->classHandle())) {
1371 Stats::inc(Stats::UnitMerge_mergeable_unique_persistent_cache);
1373 Class::Avail avail = cls->avail(other, true);
1374 if (UNLIKELY(avail == Class::Avail::Fail)) {
1375 raise_error("unknown class %s", other->name()->data());
1377 assert(avail == Class::Avail::True);
1378 getDataRef<Class*>(tcbase, cls->classHandle()) = cls;
1379 if (debugger) phpDebuggerDefClassHook(cls);
1380 obj = mi->mergeableObj(++ix);
1381 k = UnitMergeKind(uintptr_t(obj) & 7);
1382 } while (k == UnitMergeKindUniqueDefinedClass);
1383 continue;
1385 case UnitMergeKindPersistentDefine:
1386 // will be removed by compactUnitMergeInfo
1387 // but could be hit by other threads before
1388 // that happens
1389 do {
1390 ix += 1 + sizeof(TypedValueAux) / sizeof(void*);
1391 obj = mi->mergeableObj(ix);
1392 k = UnitMergeKind(uintptr_t(obj) & 7);
1393 } while (k == UnitMergeKindDefine);
1394 continue;
1396 case UnitMergeKindDefine:
1397 do {
1398 Stats::inc(Stats::UnitMerge_mergeable);
1399 Stats::inc(Stats::UnitMerge_mergeable_define);
1400 StringData* name = (StringData*)((char*)obj - (int)k);
1401 auto* v = (TypedValueAux*)mi->mergeableData(ix + 1);
1402 assert(v->m_type != KindOfUninit);
1403 mergeCns(getDataRef<TypedValue>(tcbase, v->rdsHandle()), v, name);
1404 ix += 1 + sizeof(*v) / sizeof(void*);
1405 obj = mi->mergeableObj(ix);
1406 k = UnitMergeKind(uintptr_t(obj) & 7);
1407 } while (k == UnitMergeKindDefine);
1408 continue;
1410 case UnitMergeKindGlobal:
1411 do {
1412 Stats::inc(Stats::UnitMerge_mergeable);
1413 Stats::inc(Stats::UnitMerge_mergeable_global);
1414 StringData* name = (StringData*)((char*)obj - (int)k);
1415 auto* v = (TypedValueAux*)mi->mergeableData(ix + 1);
1416 setGlobal(name, v);
1417 ix += 1 + sizeof(*v) / sizeof(void*);
1418 obj = mi->mergeableObj(ix);
1419 k = UnitMergeKind(uintptr_t(obj) & 7);
1420 } while (k == UnitMergeKindGlobal);
1421 continue;
1423 case UnitMergeKindReqDoc:
1424 do {
1425 Stats::inc(Stats::UnitMerge_mergeable);
1426 Stats::inc(Stats::UnitMerge_mergeable_require);
1427 Unit *unit = (Unit*)((char*)obj - (int)k);
1428 unsigned char& unitLoadedFlags =
1429 getDataRef<unsigned char>(tcbase, unit->m_cacheOffset);
1430 if (!(unitLoadedFlags & unit->m_cacheMask)) {
1431 unitLoadedFlags |= unit->m_cacheMask;
1432 unit->mergeImpl<debugger>(tcbase, unit->m_mergeInfo);
1433 if (UNLIKELY(!unit->isMergeOnly())) {
1434 Stats::inc(Stats::PseudoMain_Reentered);
1435 TypedValue ret;
1436 VarEnv* ve = nullptr;
1437 ActRec* fp = g_context->m_fp;
1438 if (!fp) {
1439 ve = g_context->m_globalVarEnv;
1440 } else {
1441 if (fp->hasVarEnv()) {
1442 ve = fp->m_varEnv;
1443 } else {
1444 // Nothing to do. If there is no varEnv, the enclosing
1445 // file was called by fb_autoload_map, which wants a
1446 // local scope.
1449 g_context->invokeFunc(&ret, unit->getMain(), init_null_variant,
1450 nullptr, nullptr, ve);
1451 tvRefcountedDecRef(&ret);
1452 } else {
1453 Stats::inc(Stats::PseudoMain_SkipDeep);
1455 } else {
1456 Stats::inc(Stats::PseudoMain_Guarded);
1458 obj = mi->mergeableObj(++ix);
1459 k = UnitMergeKind(uintptr_t(obj) & 7);
1460 } while (isMergeKindReq(k));
1461 continue;
1462 case UnitMergeKindDone:
1463 Stats::inc(Stats::UnitMerge_mergeable, -1);
1464 assert((unsigned)ix == mi->m_mergeablesSize);
1465 if (UNLIKELY(m_mergeState & UnitMergeStateNeedsCompact)) {
1466 SimpleLock lock(unitInitLock);
1467 if (!(m_mergeState & UnitMergeStateNeedsCompact)) return;
1468 if (!redoHoistable) {
1470 * All the classes are known to be unique, and we just got
1471 * here, so all were successfully defined. We can now go
1472 * back and convert all UnitMergeKindClass entries to
1473 * UnitMergeKindUniqueDefinedClass, and all hoistable
1474 * classes to their Class*'s instead of PreClass*'s.
1476 * We can also remove any Persistent Class/Func*'s,
1477 * and any requires of modules that are (now) empty
1479 size_t delta = compactUnitMergeInfo(mi, nullptr);
1480 UnitMergeInfo* newMi = mi;
1481 if (delta) {
1482 newMi = UnitMergeInfo::alloc(mi->m_mergeablesSize - delta);
1485 * In the case where mi == newMi, there's an apparent
1486 * race here. Although we have a lock, so we're the only
1487 * ones modifying this, there could be any number of
1488 * readers. But thats ok, because it doesnt matter
1489 * whether they see the old contents or the new.
1491 compactUnitMergeInfo(mi, newMi);
1492 if (newMi != mi) {
1493 this->m_mergeInfo = newMi;
1494 Treadmill::deferredFree(mi);
1495 if (isMergeOnly() &&
1496 newMi->m_firstHoistableFunc == newMi->m_mergeablesSize) {
1497 m_mergeState |= UnitMergeStateEmpty;
1500 assert(newMi->m_firstMergeablePreClass == newMi->m_mergeablesSize ||
1501 isMergeOnly());
1503 m_mergeState &= ~UnitMergeStateNeedsCompact;
1505 return;
1507 // Normal cases should continue, KindDone returns
1508 NOT_REACHED();
1509 } while (true);
1512 Func* Unit::getMain(Class* cls /*= NULL*/) const {
1513 if (!cls) return *m_mergeInfo->funcBegin();
1514 Lock lock(s_classesMutex);
1515 if (!m_pseudoMainCache) {
1516 m_pseudoMainCache = new PseudoMainCacheMap;
1518 auto it = m_pseudoMainCache->find(cls);
1519 if (it != m_pseudoMainCache->end()) {
1520 return it->second;
1522 Func* f = (*m_mergeInfo->funcBegin())->clone(cls);
1523 f->setNewFuncId();
1524 f->setBaseCls(cls);
1525 (*m_pseudoMainCache)[cls] = f;
1526 return f;
1529 SourceLocTable Unit::getSourceLocTable() const {
1530 if (m_sourceLocTable.size() > 0 || m_repoId == RepoIdInvalid) {
1531 return m_sourceLocTable;
1533 Lock lock(s_classesMutex);
1534 UnitRepoProxy& urp = Repo::get().urp();
1535 urp.getSourceLocTab(m_repoId).get(m_sn, ((Unit*)this)->m_sourceLocTable);
1536 return m_sourceLocTable;
1539 LineToOffsetRangeVecMap Unit::getLineToOffsetRangeVecMap() const {
1540 if (m_lineToOffsetRangeVecMap.size() > 0) {
1541 return m_lineToOffsetRangeVecMap;
1543 auto srcLoc = this->getSourceLocTable();
1544 LineToOffsetRangeVecMap map;
1545 Offset baseOff = 0;
1546 for (size_t i = 0; i < srcLoc.size(); ++i) {
1547 Offset pastOff = srcLoc[i].pastOffset();
1548 OffsetRange range(baseOff, pastOff);
1549 auto line0 = srcLoc[i].val().line0;
1550 auto line1 = srcLoc[i].val().line1;
1551 for (int line = line0; line <= line1; line++) {
1552 auto it = map.find(line);
1553 if (it != map.end()) {
1554 it->second.push_back(range);
1555 } else {
1556 OffsetRangeVec v(1);
1557 v.push_back(range);
1558 map[line] = v;
1561 baseOff = pastOff;
1563 const_cast<Unit*>(this)->m_lineToOffsetRangeVecMap = map;
1564 return m_lineToOffsetRangeVecMap;
1567 // This uses range lookups so offsets in the middle of instructions are
1568 // supported.
1569 int getLineNumber(const LineTable& table, Offset pc) {
1570 auto const key = LineEntry(pc, -1);
1571 auto it = std::upper_bound(begin(table), end(table), key);
1572 if (it != end(table)) {
1573 assert(pc < it->pastOffset());
1574 return it->val();
1576 return -1;
1579 int Unit::getLineNumber(Offset pc) const {
1580 return HPHP::getLineNumber(m_lineTable, pc);
1583 bool getSourceLoc(const SourceLocTable& table, Offset pc, SourceLoc& sLoc) {
1584 SourceLocEntry key(pc, sLoc);
1585 auto it = std::upper_bound(table.begin(), table.end(), key);
1586 if (it != table.end()) {
1587 assert(pc < it->pastOffset());
1588 sLoc = it->val();
1589 return true;
1591 return false;
1594 // Sets sLoc to the source location of the first source location
1595 // entry that contains pc in its range of source locations.
1596 // Returns
1597 bool Unit::getSourceLoc(Offset pc, SourceLoc& sLoc) const {
1598 auto sourceLocTable = getSourceLocTable();
1599 return HPHP::getSourceLoc(sourceLocTable, pc, sLoc);
1602 bool Unit::getOffsetRanges(int line, OffsetRangeVec& offsets) const {
1603 assert(offsets.size() == 0);
1604 auto map = this->getLineToOffsetRangeVecMap();
1605 auto it = map.find(line);
1606 if (it == map.end()) return false;
1607 offsets = it->second;
1608 return true;
1611 bool Unit::getOffsetRange(Offset pc, OffsetRange& range) const {
1612 LineEntry key = LineEntry(pc, -1);
1613 std::vector<LineEntry>::const_iterator it =
1614 upper_bound(m_lineTable.begin(), m_lineTable.end(), key);
1615 if (it != m_lineTable.end()) {
1616 assert(pc < it->pastOffset());
1617 Offset base = it == m_lineTable.begin() ? 0 : (it-1)->pastOffset();
1618 range.m_base = base;
1619 range.m_past = it->pastOffset();
1620 return true;
1622 return false;
1625 const Func* Unit::getFunc(Offset pc) const {
1626 FuncEntry key = FuncEntry(pc, nullptr);
1627 auto it = std::upper_bound(m_funcTable.begin(), m_funcTable.end(), key);
1628 if (it != m_funcTable.end()) {
1629 assert(pc < it->pastOffset());
1630 return it->val();
1632 return nullptr;
1635 void Unit::prettyPrint(std::ostream& out, PrintOpts opts) const {
1636 auto startOffset = opts.startOffset != kInvalidOffset
1637 ? opts.startOffset : 0;
1638 auto stopOffset = opts.stopOffset != kInvalidOffset
1639 ? opts.stopOffset : m_bclen;
1641 std::map<Offset,const Func*> funcMap;
1642 for (FuncRange fr(funcs()); !fr.empty();) {
1643 const Func* f = fr.popFront();
1644 funcMap[f->base()] = f;
1646 for (PreClassPtrVec::const_iterator it = m_preClasses.begin();
1647 it != m_preClasses.end(); ++it) {
1648 Func* const* methods = (*it)->methods();
1649 size_t const numMethods = (*it)->numMethods();
1650 for (size_t i = 0; i < numMethods; ++i) {
1651 funcMap[methods[i]->base()] = methods[i];
1655 auto funcIt = funcMap.lower_bound(startOffset);
1657 const auto* it = &m_bc[startOffset];
1658 int prevLineNum = -1;
1659 MetaHandle metaHand;
1660 while (it < &m_bc[stopOffset]) {
1661 assert(funcIt == funcMap.end() ||
1662 funcIt->first >= offsetOf(it));
1663 if (opts.showFuncs) {
1664 if (funcIt != funcMap.end() &&
1665 funcIt->first == offsetOf(it)) {
1666 out.put('\n');
1667 funcIt->second->prettyPrint(out);
1668 ++funcIt;
1672 if (opts.showLines) {
1673 int lineNum = getLineNumber(offsetOf(it));
1674 if (lineNum != prevLineNum) {
1675 out << " // line " << lineNum << std::endl;
1676 prevLineNum = lineNum;
1680 out << std::string(opts.indentSize, ' ')
1681 << std::setw(4) << (it - m_bc) << ": ";
1682 out << instrToString((Op*)it, this);
1683 if (metaHand.findMeta(this, offsetOf(it))) {
1684 out << " #";
1685 Unit::MetaInfo info;
1686 while (metaHand.nextArg(info)) {
1687 int arg = info.m_arg & ~MetaInfo::VectorArg;
1688 const char *argKind = info.m_arg & MetaInfo::VectorArg ? "M" : "";
1689 switch (info.m_kind) {
1690 case Unit::MetaInfo::Kind::DataTypeInferred:
1691 case Unit::MetaInfo::Kind::DataTypePredicted:
1692 out << " i" << argKind << arg
1693 << ":t=" << tname(DataType(info.m_data));
1694 if (info.m_kind == Unit::MetaInfo::Kind::DataTypePredicted) {
1695 out << "*";
1697 break;
1698 case Unit::MetaInfo::Kind::Class: {
1699 const StringData* sd = lookupLitstrId(info.m_data);
1700 out << " i" << argKind << arg << ":c=" << sd->data();
1701 break;
1703 case Unit::MetaInfo::Kind::MVecPropClass: {
1704 const StringData* sd = lookupLitstrId(info.m_data);
1705 out << " i" << argKind << arg << ":pc=" << sd->data();
1706 break;
1708 case Unit::MetaInfo::Kind::GuardedThis:
1709 out << " GuardedThis";
1710 break;
1711 case Unit::MetaInfo::Kind::GuardedCls:
1712 out << " GuardedCls";
1713 break;
1714 case Unit::MetaInfo::Kind::None:
1715 assert(false);
1716 break;
1720 out << std::endl;
1721 it += instrLen((Op*)it);
1725 std::string Unit::toString() const {
1726 std::ostringstream ss;
1727 prettyPrint(ss);
1728 for (auto& pc : m_preClasses) {
1729 pc->prettyPrint(ss);
1731 for (FuncRange fr(funcs()); !fr.empty();) {
1732 fr.popFront()->prettyPrint(ss);
1734 return ss.str();
1737 Func* Unit::lookupFunc(const NamedEntity* ne) {
1738 return ne->getCachedFunc();
1741 Func* Unit::lookupFunc(const StringData* funcName) {
1742 const NamedEntity* ne = GetNamedEntity(funcName);
1743 return ne->getCachedFunc();
1746 Func* Unit::loadFunc(const NamedEntity* ne, const StringData* funcName) {
1747 Func* func = ne->getCachedFunc();
1748 if (LIKELY(func != nullptr)) return func;
1749 if (AutoloadHandler::s_instance->autoloadFunc(
1750 const_cast<StringData*>(funcName))) {
1751 func = ne->getCachedFunc();
1753 return func;
1756 Func* Unit::loadFunc(const StringData* name) {
1757 String normStr;
1758 auto ne = GetNamedEntity(name, true, &normStr);
1759 if (normStr) {
1760 name = normStr.get();
1762 return loadFunc(ne, name);
1765 //=============================================================================
1766 // UnitRepoProxy.
1768 UnitRepoProxy::UnitRepoProxy(Repo& repo)
1769 : RepoProxy(repo)
1770 #define URP_OP(c, o) \
1771 , m_##o##Local(repo, RepoIdLocal), m_##o##Central(repo, RepoIdCentral)
1772 URP_OPS
1773 #undef URP_OP
1775 #define URP_OP(c, o) \
1776 m_##o[RepoIdLocal] = &m_##o##Local; \
1777 m_##o[RepoIdCentral] = &m_##o##Central;
1778 URP_OPS
1779 #undef URP_OP
1782 UnitRepoProxy::~UnitRepoProxy() {
1785 void UnitRepoProxy::createSchema(int repoId, RepoTxn& txn) {
1787 std::stringstream ssCreate;
1788 ssCreate << "CREATE TABLE " << m_repo.table(repoId, "Unit")
1789 << "(unitSn INTEGER PRIMARY KEY, md5 BLOB, bc BLOB,"
1790 " bc_meta BLOB, mainReturn BLOB, mergeable INTEGER,"
1791 "lines BLOB, typeAliases BLOB, UNIQUE (md5));";
1792 txn.exec(ssCreate.str());
1795 std::stringstream ssCreate;
1796 ssCreate << "CREATE TABLE " << m_repo.table(repoId, "UnitLitstr")
1797 << "(unitSn INTEGER, litstrId INTEGER, litstr TEXT,"
1798 " PRIMARY KEY (unitSn, litstrId));";
1799 txn.exec(ssCreate.str());
1802 std::stringstream ssCreate;
1803 ssCreate << "CREATE TABLE " << m_repo.table(repoId, "UnitArray")
1804 << "(unitSn INTEGER, arrayId INTEGER, array BLOB,"
1805 " PRIMARY KEY (unitSn, arrayId));";
1806 txn.exec(ssCreate.str());
1809 std::stringstream ssCreate;
1810 ssCreate << "CREATE TABLE " << m_repo.table(repoId, "UnitMergeables")
1811 << "(unitSn INTEGER, mergeableIx INTEGER,"
1812 " mergeableKind INTEGER, mergeableId INTEGER,"
1813 " mergeableValue BLOB,"
1814 " PRIMARY KEY (unitSn, mergeableIx));";
1815 txn.exec(ssCreate.str());
1818 std::stringstream ssCreate;
1819 ssCreate << "CREATE TABLE " << m_repo.table(repoId, "UnitSourceLoc")
1820 << "(unitSn INTEGER, pastOffset INTEGER, line0 INTEGER,"
1821 " char0 INTEGER, line1 INTEGER, char1 INTEGER,"
1822 " PRIMARY KEY (unitSn, pastOffset));";
1823 txn.exec(ssCreate.str());
1827 bool UnitRepoProxy::loadHelper(UnitEmitter& ue,
1828 const std::string& name,
1829 const MD5& md5) {
1830 ue.setFilepath(makeStaticString(name));
1831 // Look for a repo that contains a unit with matching MD5.
1832 int repoId;
1833 for (repoId = RepoIdCount - 1; repoId >= 0; --repoId) {
1834 if (!getUnit(repoId).get(ue, md5)) {
1835 break;
1838 if (repoId < 0) {
1839 TRACE(3, "No repo contains '%s' (0x%016" PRIx64 "%016" PRIx64 ")\n",
1840 name.c_str(), md5.q[0], md5.q[1]);
1841 return false;
1843 try {
1844 getUnitLitstrs(repoId).get(ue);
1845 getUnitArrays(repoId).get(ue);
1846 m_repo.pcrp().getPreClasses(repoId).get(ue);
1847 getUnitMergeables(repoId).get(ue);
1848 m_repo.frp().getFuncs(repoId).get(ue);
1849 } catch (RepoExc& re) {
1850 TRACE(0,
1851 "Repo error loading '%s' (0x%016" PRIx64 "%016"
1852 PRIx64 ") from '%s': %s\n",
1853 name.c_str(), md5.q[0], md5.q[1], m_repo.repoName(repoId).c_str(),
1854 re.msg().c_str());
1855 return false;
1857 TRACE(3, "Repo loaded '%s' (0x%016" PRIx64 "%016" PRIx64 ") from '%s'\n",
1858 name.c_str(), md5.q[0], md5.q[1], m_repo.repoName(repoId).c_str());
1859 return true;
1862 std::unique_ptr<UnitEmitter>
1863 UnitRepoProxy::loadEmitter(const std::string& name, const MD5& md5) {
1864 auto ue = folly::make_unique<UnitEmitter>(md5);
1865 if (!loadHelper(*ue, name, md5)) ue.reset();
1866 return ue;
1869 Unit* UnitRepoProxy::load(const std::string& name, const MD5& md5) {
1870 UnitEmitter ue(md5);
1871 if (!loadHelper(ue, name, md5)) return nullptr;
1872 return ue.create();
1875 void UnitRepoProxy::InsertUnitStmt
1876 ::insert(RepoTxn& txn, int64_t& unitSn, const MD5& md5,
1877 const unsigned char* bc, size_t bclen,
1878 const unsigned char* bc_meta, size_t bc_meta_len,
1879 const TypedValue* mainReturn, bool mergeOnly,
1880 const LineTable& lines,
1881 const std::vector<TypeAlias>& typeAliases) {
1882 BlobEncoder linesBlob;
1883 BlobEncoder typeAliasesBlob;
1885 if (!prepared()) {
1886 std::stringstream ssInsert;
1887 ssInsert << "INSERT INTO " << m_repo.table(m_repoId, "Unit")
1888 << " VALUES(NULL, @md5, @bc, @bc_meta,"
1889 " @mainReturn, @mergeable, @lines, @typeAliases);";
1890 txn.prepare(*this, ssInsert.str());
1892 RepoTxnQuery query(txn, *this);
1893 query.bindMd5("@md5", md5);
1894 query.bindBlob("@bc", (const void*)bc, bclen);
1895 query.bindBlob("@bc_meta",
1896 bc_meta_len ? (const void*)bc_meta : (const void*)"",
1897 bc_meta_len);
1898 query.bindTypedValue("@mainReturn", *mainReturn);
1899 query.bindBool("@mergeable", mergeOnly);
1900 query.bindBlob("@lines", linesBlob(lines), /* static */ true);
1901 query.bindBlob("@typeAliases",
1902 typeAliasesBlob(typeAliases), /* static */ true);
1903 query.exec();
1904 unitSn = query.getInsertedRowid();
1907 bool UnitRepoProxy::GetUnitStmt
1908 ::get(UnitEmitter& ue, const MD5& md5) {
1909 try {
1910 RepoTxn txn(m_repo);
1911 if (!prepared()) {
1912 std::stringstream ssSelect;
1913 ssSelect << "SELECT unitSn,bc,bc_meta,mainReturn,mergeable,"
1914 "lines,typeAliases FROM "
1915 << m_repo.table(m_repoId, "Unit")
1916 << " WHERE md5 == @md5;";
1917 txn.prepare(*this, ssSelect.str());
1919 RepoTxnQuery query(txn, *this);
1920 query.bindMd5("@md5", md5);
1921 query.step();
1922 if (!query.row()) {
1923 return true;
1925 int64_t unitSn; /**/ query.getInt64(0, unitSn);
1926 const void* bc; size_t bclen; /**/ query.getBlob(1, bc, bclen);
1927 const void* bc_meta; size_t bc_meta_len; /**/ query.getBlob(2, bc_meta,
1928 bc_meta_len);
1929 TypedValue value; /**/ query.getTypedValue(3, value);
1930 bool mergeable; /**/ query.getBool(4, mergeable);
1931 BlobDecoder linesBlob = /**/ query.getBlob(5);
1932 BlobDecoder typeAliasesBlob = /**/ query.getBlob(6);
1933 ue.setRepoId(m_repoId);
1934 ue.setSn(unitSn);
1935 ue.setBc((const unsigned char*)bc, bclen);
1936 ue.setBcMeta((const unsigned char*)bc_meta, bc_meta_len);
1937 ue.setMainReturn(&value);
1938 ue.setMergeOnly(mergeable);
1940 LineTable lines;
1941 linesBlob(lines);
1942 ue.setLines(lines);
1944 typeAliasesBlob(ue.m_typeAliases);
1946 txn.commit();
1947 } catch (RepoExc& re) {
1948 return true;
1950 return false;
1953 void UnitRepoProxy::InsertUnitLitstrStmt
1954 ::insert(RepoTxn& txn, int64_t unitSn, Id litstrId,
1955 const StringData* litstr) {
1956 if (!prepared()) {
1957 std::stringstream ssInsert;
1958 ssInsert << "INSERT INTO " << m_repo.table(m_repoId, "UnitLitstr")
1959 << " VALUES(@unitSn, @litstrId, @litstr);";
1960 txn.prepare(*this, ssInsert.str());
1962 RepoTxnQuery query(txn, *this);
1963 query.bindInt64("@unitSn", unitSn);
1964 query.bindId("@litstrId", litstrId);
1965 query.bindStaticString("@litstr", litstr);
1966 query.exec();
1969 void UnitRepoProxy::GetUnitLitstrsStmt
1970 ::get(UnitEmitter& ue) {
1971 RepoTxn txn(m_repo);
1972 if (!prepared()) {
1973 std::stringstream ssSelect;
1974 ssSelect << "SELECT litstrId,litstr FROM "
1975 << m_repo.table(m_repoId, "UnitLitstr")
1976 << " WHERE unitSn == @unitSn ORDER BY litstrId ASC;";
1977 txn.prepare(*this, ssSelect.str());
1979 RepoTxnQuery query(txn, *this);
1980 query.bindInt64("@unitSn", ue.sn());
1981 do {
1982 query.step();
1983 if (query.row()) {
1984 Id litstrId; /**/ query.getId(0, litstrId);
1985 StringData* litstr; /**/ query.getStaticString(1, litstr);
1986 Id id UNUSED = ue.mergeUnitLitstr(litstr);
1987 assert(id == litstrId);
1989 } while (!query.done());
1990 txn.commit();
1993 void UnitRepoProxy::InsertUnitArrayStmt
1994 ::insert(RepoTxn& txn, int64_t unitSn, Id arrayId,
1995 const std::string& array) {
1996 if (!prepared()) {
1997 std::stringstream ssInsert;
1998 ssInsert << "INSERT INTO " << m_repo.table(m_repoId, "UnitArray")
1999 << " VALUES(@unitSn, @arrayId, @array);";
2000 txn.prepare(*this, ssInsert.str());
2002 RepoTxnQuery query(txn, *this);
2003 query.bindInt64("@unitSn", unitSn);
2004 query.bindId("@arrayId", arrayId);
2005 query.bindStdString("@array", array);
2006 query.exec();
2009 void UnitRepoProxy::GetUnitArraysStmt
2010 ::get(UnitEmitter& ue) {
2011 RepoTxn txn(m_repo);
2012 if (!prepared()) {
2013 std::stringstream ssSelect;
2014 ssSelect << "SELECT arrayId,array FROM "
2015 << m_repo.table(m_repoId, "UnitArray")
2016 << " WHERE unitSn == @unitSn ORDER BY arrayId ASC;";
2017 txn.prepare(*this, ssSelect.str());
2019 RepoTxnQuery query(txn, *this);
2020 query.bindInt64("@unitSn", ue.sn());
2021 do {
2022 query.step();
2023 if (query.row()) {
2024 Id arrayId; /**/ query.getId(0, arrayId);
2025 std::string key; /**/ query.getStdString(1, key);
2026 Variant v = unserialize_from_buffer(key.data(), key.size());
2027 Id id UNUSED = ue.mergeArray(v.asArrRef().get(), key);
2028 assert(id == arrayId);
2030 } while (!query.done());
2031 txn.commit();
2034 void UnitRepoProxy::InsertUnitMergeableStmt
2035 ::insert(RepoTxn& txn, int64_t unitSn,
2036 int ix, UnitMergeKind kind, Id id,
2037 TypedValue* value) {
2038 if (!prepared()) {
2039 std::stringstream ssInsert;
2040 ssInsert << "INSERT INTO " << m_repo.table(m_repoId, "UnitMergeables")
2041 << " VALUES(@unitSn, @mergeableIx, @mergeableKind,"
2042 " @mergeableId, @mergeableValue);";
2043 txn.prepare(*this, ssInsert.str());
2046 RepoTxnQuery query(txn, *this);
2047 query.bindInt64("@unitSn", unitSn);
2048 query.bindInt("@mergeableIx", ix);
2049 query.bindInt("@mergeableKind", (int)kind);
2050 query.bindId("@mergeableId", id);
2051 if (value) {
2052 assert(kind == UnitMergeKindDefine ||
2053 kind == UnitMergeKindPersistentDefine ||
2054 kind == UnitMergeKindGlobal);
2055 query.bindTypedValue("@mergeableValue", *value);
2056 } else {
2057 assert(kind == UnitMergeKindReqDoc);
2058 query.bindNull("@mergeableValue");
2060 query.exec();
2063 void UnitRepoProxy::GetUnitMergeablesStmt
2064 ::get(UnitEmitter& ue) {
2065 RepoTxn txn(m_repo);
2066 if (!prepared()) {
2067 std::stringstream ssSelect;
2068 ssSelect << "SELECT mergeableIx,mergeableKind,mergeableId,mergeableValue"
2069 " FROM "
2070 << m_repo.table(m_repoId, "UnitMergeables")
2071 << " WHERE unitSn == @unitSn ORDER BY mergeableIx ASC;";
2072 txn.prepare(*this, ssSelect.str());
2074 RepoTxnQuery query(txn, *this);
2075 query.bindInt64("@unitSn", ue.sn());
2076 do {
2077 query.step();
2078 if (query.row()) {
2079 int mergeableIx; /**/ query.getInt(0, mergeableIx);
2080 int mergeableKind; /**/ query.getInt(1, mergeableKind);
2081 Id mergeableId; /**/ query.getInt(2, mergeableId);
2083 if (UNLIKELY(!RuntimeOption::RepoAuthoritative)) {
2085 * We're using a repo generated in WholeProgram mode,
2086 * but we're not using it in RepoAuthoritative mode
2087 * (this is dodgy to start with). We're not going to
2088 * deal with requires at merge time, so drop them
2089 * here, and clear the mergeOnly flag for the unit.
2090 * The one exception is persistent constants are allowed in systemlib.
2092 if (mergeableKind != UnitMergeKindPersistentDefine ||
2093 SystemLib::s_inited) {
2094 ue.setMergeOnly(false);
2097 switch (mergeableKind) {
2098 case UnitMergeKindReqDoc:
2099 ue.insertMergeableInclude(mergeableIx,
2100 (UnitMergeKind)mergeableKind, mergeableId);
2101 break;
2102 case UnitMergeKindPersistentDefine:
2103 case UnitMergeKindDefine:
2104 case UnitMergeKindGlobal: {
2105 TypedValue mergeableValue; /**/ query.getTypedValue(3,
2106 mergeableValue);
2107 ue.insertMergeableDef(mergeableIx, (UnitMergeKind)mergeableKind,
2108 mergeableId, mergeableValue);
2109 break;
2113 } while (!query.done());
2114 txn.commit();
2117 void UnitRepoProxy::InsertUnitSourceLocStmt
2118 ::insert(RepoTxn& txn, int64_t unitSn, Offset pastOffset,
2119 int line0, int char0, int line1, int char1) {
2120 if (!prepared()) {
2121 std::stringstream ssInsert;
2122 ssInsert << "INSERT INTO " << m_repo.table(m_repoId, "UnitSourceLoc")
2123 << " VALUES(@unitSn, @pastOffset, @line0, @char0, @line1,"
2124 " @char1);";
2125 txn.prepare(*this, ssInsert.str());
2127 RepoTxnQuery query(txn, *this);
2128 query.bindInt64("@unitSn", unitSn);
2129 query.bindOffset("@pastOffset", pastOffset);
2130 query.bindInt("@line0", line0);
2131 query.bindInt("@char0", char0);
2132 query.bindInt("@line1", line1);
2133 query.bindInt("@char1", char1);
2134 query.exec();
2137 bool UnitRepoProxy::GetSourceLocTabStmt
2138 ::get(int64_t unitSn, SourceLocTable& sourceLocTab) {
2139 try {
2140 RepoTxn txn(m_repo);
2141 if (!prepared()) {
2142 std::stringstream ssSelect;
2143 ssSelect << "SELECT pastOffset,line0,char0,line1,char1 FROM "
2144 << m_repo.table(m_repoId, "UnitSourceLoc")
2145 << " WHERE unitSn == @unitSn"
2146 " ORDER BY pastOffset ASC;";
2147 txn.prepare(*this, ssSelect.str());
2149 RepoTxnQuery query(txn, *this);
2150 query.bindInt64("@unitSn", unitSn);
2151 do {
2152 query.step();
2153 if (!query.row()) {
2154 return true;
2156 Offset pastOffset;
2157 query.getOffset(0, pastOffset);
2158 SourceLoc sLoc;
2159 query.getInt(1, sLoc.line0);
2160 query.getInt(2, sLoc.char0);
2161 query.getInt(3, sLoc.line1);
2162 query.getInt(4, sLoc.char1);
2163 SourceLocEntry entry(pastOffset, sLoc);
2164 sourceLocTab.push_back(entry);
2165 } while (!query.done());
2166 txn.commit();
2167 } catch (RepoExc& re) {
2168 return true;
2170 return false;
2173 //=============================================================================
2174 // UnitEmitter.
2176 UnitEmitter::UnitEmitter(const MD5& md5)
2177 : m_repoId(-1), m_sn(-1), m_bcmax(BCMaxInit), m_bc((unsigned char*)malloc(BCMaxInit)),
2178 m_bclen(0), m_bc_meta(nullptr), m_bc_meta_len(0), m_filepath(nullptr),
2179 m_md5(md5), m_nextFuncSn(0), m_mergeOnly(false),
2180 m_allClassesHoistable(true), m_returnSeen(false) {
2181 tvWriteUninit(&m_mainReturn);
2184 UnitEmitter::~UnitEmitter() {
2185 if (m_bc) {
2186 free(m_bc);
2188 if (m_bc_meta) {
2189 free(m_bc_meta);
2191 for (FeVec::const_iterator it = m_fes.begin(); it != m_fes.end(); ++it) {
2192 delete *it;
2194 for (PceVec::const_iterator it = m_pceVec.begin(); it != m_pceVec.end();
2195 ++it) {
2196 delete *it;
2200 void UnitEmitter::addTrivialPseudoMain() {
2201 initMain(0, 0);
2202 FuncEmitter* mfe = getMain();
2203 emitOp(OpInt);
2204 emitInt64(1);
2205 emitOp(OpRetC);
2206 mfe->setMaxStackCells(1);
2207 mfe->finish(bcPos(), false);
2208 recordFunction(mfe);
2210 TypedValue mainReturn;
2211 mainReturn.m_data.num = 1;
2212 mainReturn.m_type = KindOfInt64;
2213 setMainReturn(&mainReturn);
2214 setMergeOnly(true);
2217 void UnitEmitter::setBc(const unsigned char* bc, size_t bclen) {
2218 if (m_bc) {
2219 free(m_bc);
2221 m_bc = (unsigned char*)malloc(bclen);
2222 m_bcmax = bclen;
2223 memcpy(m_bc, bc, bclen);
2224 m_bclen = bclen;
2227 void UnitEmitter::setBcMeta(const unsigned char* bc_meta, size_t bc_meta_len) {
2228 assert(m_bc_meta == nullptr);
2229 if (bc_meta_len) {
2230 m_bc_meta = (unsigned char*)malloc(bc_meta_len);
2231 memcpy(m_bc_meta, bc_meta, bc_meta_len);
2233 m_bc_meta_len = bc_meta_len;
2236 void UnitEmitter::setLines(const LineTable& lines) {
2237 this->m_lineTable = lines;
2240 Id UnitEmitter::mergeUnitLitstr(const StringData* litstr) {
2241 auto it = m_litstr2id.find(litstr);
2242 if (it == m_litstr2id.end()) {
2243 const StringData* str = makeStaticString(litstr);
2244 Id id = m_litstrs.size();
2245 m_litstrs.push_back(str);
2246 m_litstr2id[str] = id;
2247 return id;
2248 } else {
2249 return it->second;
2253 Id UnitEmitter::mergeLitstr(const StringData* litstr) {
2254 if (Option::WholeProgram) {
2255 return encodeGlobalLitstrId(LitstrTable::get().mergeLitstr(litstr));
2257 return mergeUnitLitstr(litstr);
2260 Id UnitEmitter::mergeArray(const ArrayData* a) {
2261 Variant v(const_cast<ArrayData*>(a));
2262 auto key = HHVM_FN(serialize)(v).toCppString();
2263 return mergeArray(a, key);
2266 Id UnitEmitter::mergeArray(const ArrayData* a, const std::string& key) {
2267 ArrayIdMap::const_iterator it = m_array2id.find(key);
2268 if (it != m_array2id.end()) {
2269 return it->second;
2271 a = ArrayData::GetScalarArray(const_cast<ArrayData*>(a), key);
2272 Id id = m_arrays.size();
2273 ArrayVecElm ave = {key, a};
2274 m_arrays.push_back(ave);
2275 m_array2id[key] = id;
2276 return id;
2279 const StringData* UnitEmitter::lookupLitstr(Id id) const {
2280 if (isGlobalLitstrId(id)) {
2281 return LitstrTable::get().lookupLitstrId(decodeGlobalLitstrId(id));
2283 assert(id < m_litstrs.size());
2284 return m_litstrs[id];
2287 const ArrayData* UnitEmitter::lookupArray(Id id) const {
2288 assert(id < m_arrays.size());
2289 return m_arrays[id].array;
2292 FuncEmitter* UnitEmitter::getMain() {
2293 return m_fes[0];
2296 void UnitEmitter::initMain(int line1, int line2) {
2297 assert(m_fes.size() == 0);
2298 StringData* name = makeStaticString("");
2299 FuncEmitter* pseudomain = newFuncEmitter(name);
2300 Attr attrs = AttrMayUseVV;
2301 pseudomain->init(line1, line2, 0, attrs, false, name);
2304 FuncEmitter* UnitEmitter::newFuncEmitter(const StringData* n) {
2305 assert(m_fes.size() > 0 || !strcmp(n->data(), "")); // Pseudomain comes first.
2306 FuncEmitter* fe = new FuncEmitter(*this, m_nextFuncSn++, m_fes.size(), n);
2307 m_fes.push_back(fe);
2308 return fe;
2311 void UnitEmitter::appendTopEmitter(FuncEmitter* fe) {
2312 fe->setIds(m_nextFuncSn++, m_fes.size());
2313 m_fes.push_back(fe);
2316 void UnitEmitter::pushMergeableClass(PreClassEmitter* e) {
2317 m_mergeableStmts.push_back(std::make_pair(UnitMergeKindClass, e->id()));
2320 void UnitEmitter::pushMergeableInclude(UnitMergeKind kind,
2321 const StringData* unitName) {
2322 m_mergeableStmts.push_back(
2323 std::make_pair(kind, mergeLitstr(unitName)));
2324 m_allClassesHoistable = false;
2327 void UnitEmitter::insertMergeableInclude(int ix, UnitMergeKind kind, int id) {
2328 assert(size_t(ix) <= m_mergeableStmts.size());
2329 m_mergeableStmts.insert(m_mergeableStmts.begin() + ix,
2330 std::make_pair(kind, id));
2331 m_allClassesHoistable = false;
2334 void UnitEmitter::pushMergeableDef(UnitMergeKind kind,
2335 const StringData* name,
2336 const TypedValue& tv) {
2337 m_mergeableStmts.push_back(std::make_pair(kind, m_mergeableValues.size()));
2338 m_mergeableValues.push_back(std::make_pair(mergeLitstr(name), tv));
2339 m_allClassesHoistable = false;
2342 void UnitEmitter::insertMergeableDef(int ix, UnitMergeKind kind,
2343 Id id, const TypedValue& tv) {
2344 assert(size_t(ix) <= m_mergeableStmts.size());
2345 m_mergeableStmts.insert(m_mergeableStmts.begin() + ix,
2346 std::make_pair(kind, m_mergeableValues.size()));
2347 m_mergeableValues.push_back(std::make_pair(id, tv));
2348 m_allClassesHoistable = false;
2351 FuncEmitter* UnitEmitter::newMethodEmitter(const StringData* n,
2352 PreClassEmitter* pce) {
2353 return new FuncEmitter(*this, m_nextFuncSn++, n, pce);
2356 PreClassEmitter* UnitEmitter::newPreClassEmitter(const StringData* n,
2357 PreClass::Hoistable
2358 hoistable) {
2359 // See class.h for information about hoistability.
2360 if (hoistable && m_hoistablePreClassSet.count(n)) {
2361 hoistable = PreClass::Mergeable;
2364 PreClassEmitter* pce = new PreClassEmitter(*this, m_pceVec.size(), n,
2365 hoistable);
2367 if (hoistable >= PreClass::MaybeHoistable) {
2368 m_hoistablePreClassSet.insert(n);
2369 if (hoistable == PreClass::ClosureHoistable) {
2370 // Closures should appear at the VERY top of the file, so if any class in
2371 // the same file tries to use them, they are already defined. We had a
2372 // fun race where one thread was autoloading a file, finished parsing the
2373 // class, then another thread came along and saw the class was already
2374 // loaded and ran it before the first thread had time to parse the
2375 // closure class.
2376 m_hoistablePceIdList.push_front(pce->id());
2377 } else {
2378 m_hoistablePceIdList.push_back(pce->id());
2380 } else {
2381 m_allClassesHoistable = false;
2383 if (hoistable >= PreClass::Mergeable &&
2384 hoistable < PreClass::AlwaysHoistable) {
2385 if (m_returnSeen) {
2386 m_allClassesHoistable = false;
2387 } else {
2388 pushMergeableClass(pce);
2391 m_pceVec.push_back(pce);
2392 return pce;
2395 Id UnitEmitter::addTypeAlias(const TypeAlias& td) {
2396 Id id = m_typeAliases.size();
2397 m_typeAliases.push_back(td);
2398 return id;
2401 void UnitEmitter::recordSourceLocation(const Location* sLoc, Offset start) {
2402 // Some byte codes, such as for the implicit "return 0" at the end of a
2403 // a source file do not have valid source locations. This check makes
2404 // sure we don't record a (dummy) source location in this case.
2405 if (start > 0 && sLoc->line0 == 1 && sLoc->char0 == 1 &&
2406 sLoc->line1 == 1 && sLoc->char1 == 1 && strlen(sLoc->file) == 0) return;
2407 SourceLoc newLoc(*sLoc);
2408 if (!m_sourceLocTab.empty()) {
2409 if (m_sourceLocTab.back().second == newLoc) {
2410 // Combine into the interval already at the back of the vector.
2411 assert(start >= m_sourceLocTab.back().first);
2412 return;
2414 assert(m_sourceLocTab.back().first < start &&
2415 "source location offsets must be added to UnitEmitter in "
2416 "increasing order");
2417 } else {
2418 // First record added should be for bytecode offset zero.
2419 assert(start == 0);
2421 m_sourceLocTab.push_back(std::make_pair(start, newLoc));
2424 void UnitEmitter::recordFunction(FuncEmitter* fe) {
2425 m_feTab.push_back(std::make_pair(fe->past(), fe));
2428 Func* UnitEmitter::newFunc(const FuncEmitter* fe, Unit& unit,
2429 Id id, PreClass* preClass, int line1, int line2,
2430 Offset base, Offset past,
2431 const StringData* name, Attr attrs, bool top,
2432 const StringData* docComment, int numParams,
2433 bool needsNextClonedClosure) {
2434 Func* f = new (Func::allocFuncMem(name, numParams,
2435 needsNextClonedClosure,
2436 !preClass))
2437 Func(unit, id, preClass, line1, line2, base, past, name,
2438 attrs, top, docComment, numParams);
2439 m_fMap[fe] = f;
2440 return f;
2443 static LineTable createLineTable(
2444 std::vector<std::pair<Offset,SourceLoc> >& srcLoc,
2445 Offset bclen) {
2446 LineTable lines;
2447 for (size_t i = 0; i < srcLoc.size(); ++i) {
2448 Offset endOff = i < srcLoc.size() - 1 ? srcLoc[i + 1].first : bclen;
2449 lines.push_back(LineEntry(endOff, srcLoc[i].second.line1));
2451 return lines;
2454 SourceLocTable UnitEmitter::createSourceLocTable() const {
2455 SourceLocTable locations;
2456 for (size_t i = 0; i < m_sourceLocTab.size(); ++i) {
2457 Offset endOff = i < m_sourceLocTab.size() - 1
2458 ? m_sourceLocTab[i + 1].first
2459 : m_bclen;
2460 locations.push_back(SourceLocEntry(endOff, m_sourceLocTab[i].second));
2462 return locations;
2465 static LineToOffsetRangeVecMap createLineToOffsetMap(
2466 std::vector<std::pair<Offset,SourceLoc> >& srcLoc,
2467 Offset bclen) {
2468 LineToOffsetRangeVecMap map;
2469 for (size_t i = 0; i < srcLoc.size(); ++i) {
2470 Offset baseOff = srcLoc[i].first;
2471 Offset endOff = i < srcLoc.size() - 1 ? srcLoc[i + 1].first : bclen;
2472 OffsetRange range(baseOff, endOff);
2473 auto line0 = srcLoc[i].second.line0;
2474 auto line1 = srcLoc[i].second.line1;
2475 for (int line = line0; line <= line1; line++) {
2476 auto it = map.find(line);
2477 if (it != map.end()) {
2478 it->second.push_back(range);
2479 } else {
2480 OffsetRangeVec v(1);
2481 v.push_back(range);
2482 map[line] = v;
2486 return map;
2489 bool UnitEmitter::insert(UnitOrigin unitOrigin, RepoTxn& txn) {
2490 Repo& repo = Repo::get();
2491 UnitRepoProxy& urp = repo.urp();
2492 int repoId = Repo::get().repoIdForNewUnit(unitOrigin);
2493 if (repoId == RepoIdInvalid) {
2494 return true;
2496 m_repoId = repoId;
2498 try {
2500 auto lines = createLineTable(m_sourceLocTab, m_bclen);
2501 urp.insertUnit(repoId).insert(txn, m_sn, m_md5, m_bc, m_bclen,
2502 m_bc_meta, m_bc_meta_len,
2503 &m_mainReturn, m_mergeOnly, lines,
2504 m_typeAliases);
2506 int64_t usn = m_sn;
2507 for (unsigned i = 0; i < m_litstrs.size(); ++i) {
2508 urp.insertUnitLitstr(repoId).insert(txn, usn, i, m_litstrs[i]);
2510 for (unsigned i = 0; i < m_arrays.size(); ++i) {
2511 urp.insertUnitArray(repoId).insert(txn, usn, i, m_arrays[i].serialized);
2513 for (auto it = m_fes.begin(); it != m_fes.end(); ++it) {
2514 (*it)->commit(txn);
2516 for (auto it = m_pceVec.begin(); it != m_pceVec.end(); ++it) {
2517 (*it)->commit(txn);
2520 for (int i = 0, n = m_mergeableStmts.size(); i < n; i++) {
2521 switch (m_mergeableStmts[i].first) {
2522 case UnitMergeKindDone:
2523 case UnitMergeKindUniqueDefinedClass:
2524 not_reached();
2525 case UnitMergeKindClass: break;
2526 case UnitMergeKindReqDoc: {
2527 urp.insertUnitMergeable(repoId).insert(
2528 txn, usn, i,
2529 m_mergeableStmts[i].first, m_mergeableStmts[i].second, nullptr);
2530 break;
2532 case UnitMergeKindDefine:
2533 case UnitMergeKindPersistentDefine:
2534 case UnitMergeKindGlobal: {
2535 int ix = m_mergeableStmts[i].second;
2536 urp.insertUnitMergeable(repoId).insert(
2537 txn, usn, i,
2538 m_mergeableStmts[i].first,
2539 m_mergeableValues[ix].first, &m_mergeableValues[ix].second);
2540 break;
2544 if (RuntimeOption::RepoDebugInfo) {
2545 for (size_t i = 0; i < m_sourceLocTab.size(); ++i) {
2546 SourceLoc& e = m_sourceLocTab[i].second;
2547 Offset endOff = i < m_sourceLocTab.size() - 1
2548 ? m_sourceLocTab[i + 1].first
2549 : m_bclen;
2551 urp.insertUnitSourceLoc(repoId)
2552 .insert(txn, usn, endOff, e.line0, e.char0, e.line1, e.char1);
2555 return false;
2556 } catch (RepoExc& re) {
2557 TRACE(3, "Failed to commit '%s' (0x%016" PRIx64 "%016" PRIx64 ") to '%s': %s\n",
2558 m_filepath->data(), m_md5.q[0], m_md5.q[1],
2559 repo.repoName(repoId).c_str(), re.msg().c_str());
2560 return true;
2564 void UnitEmitter::commit(UnitOrigin unitOrigin) {
2565 Repo& repo = Repo::get();
2566 try {
2567 RepoTxn txn(repo);
2568 bool err = insert(unitOrigin, txn);
2569 if (!err) {
2570 txn.commit();
2572 } catch (RepoExc& re) {
2573 int repoId = repo.repoIdForNewUnit(unitOrigin);
2574 if (repoId != RepoIdInvalid) {
2575 TRACE(3, "Failed to commit '%s' (0x%016" PRIx64 "%016" PRIx64 ") to '%s': %s\n",
2576 m_filepath->data(), m_md5.q[0], m_md5.q[1],
2577 repo.repoName(repoId).c_str(), re.msg().c_str());
2582 Unit* UnitEmitter::create() {
2583 Unit* u = new Unit();
2584 u->m_repoId = m_repoId;
2585 u->m_sn = m_sn;
2586 u->m_bc = allocateBCRegion(m_bc, m_bclen);
2587 u->m_bclen = m_bclen;
2588 if (m_bc_meta_len) {
2589 u->m_bc_meta = allocateBCRegion(m_bc_meta, m_bc_meta_len);
2590 u->m_bc_meta_len = m_bc_meta_len;
2592 u->m_filepath = m_filepath;
2593 u->m_mainReturn = m_mainReturn;
2594 u->m_mergeOnly = m_mergeOnly;
2596 const std::string& dirname = FileUtil::safe_dirname(m_filepath->data(),
2597 m_filepath->size());
2598 u->m_dirpath = makeStaticString(dirname);
2600 u->m_md5 = m_md5;
2601 for (unsigned i = 0; i < m_litstrs.size(); ++i) {
2602 NamedEntityPair np;
2603 np.first = m_litstrs[i];
2604 np.second = nullptr;
2605 u->m_namedInfo.push_back(np);
2607 for (unsigned i = 0; i < m_arrays.size(); ++i) {
2608 u->m_arrays.push_back(m_arrays[i].array);
2610 for (PceVec::const_iterator it = m_pceVec.begin(); it != m_pceVec.end();
2611 ++it) {
2612 u->m_preClasses.push_back(PreClassPtr((*it)->create(*u)));
2614 u->m_typeAliases = m_typeAliases;
2616 size_t ix = m_fes.size() + m_hoistablePceIdList.size();
2617 if (m_mergeOnly && !m_allClassesHoistable) {
2618 size_t extra = 0;
2619 for (auto it = m_mergeableStmts.begin();
2620 it != m_mergeableStmts.end(); ++it) {
2621 extra++;
2622 if (!RuntimeOption::RepoAuthoritative && SystemLib::s_inited) {
2623 if (it->first != UnitMergeKindClass) {
2624 extra = 0;
2625 u->m_mergeOnly = false;
2626 break;
2628 } else switch (it->first) {
2629 case UnitMergeKindPersistentDefine:
2630 case UnitMergeKindDefine:
2631 case UnitMergeKindGlobal:
2632 extra += sizeof(TypedValueAux) / sizeof(void*);
2633 break;
2634 default:
2635 break;
2638 ix += extra;
2640 UnitMergeInfo *mi = UnitMergeInfo::alloc(ix);
2641 u->m_mergeInfo = mi;
2642 ix = 0;
2643 for (auto it = m_fes.begin(); it != m_fes.end(); ++it) {
2644 Func* func = (*it)->create(*u);
2645 if (func->top()) {
2646 if (!mi->m_firstHoistableFunc) {
2647 mi->m_firstHoistableFunc = ix;
2649 } else {
2650 assert(!mi->m_firstHoistableFunc);
2652 mi->mergeableObj(ix++) = func;
2654 assert(u->getMain()->isPseudoMain());
2655 if (!mi->m_firstHoistableFunc) {
2656 mi->m_firstHoistableFunc = ix;
2658 mi->m_firstHoistablePreClass = ix;
2659 assert(m_fes.size());
2660 for (auto& id : m_hoistablePceIdList) {
2661 mi->mergeableObj(ix++) = u->m_preClasses[id].get();
2663 mi->m_firstMergeablePreClass = ix;
2664 if (u->m_mergeOnly && !m_allClassesHoistable) {
2665 for (auto it = m_mergeableStmts.begin();
2666 it != m_mergeableStmts.end(); ++it) {
2667 switch (it->first) {
2668 case UnitMergeKindClass:
2669 mi->mergeableObj(ix++) = u->m_preClasses[it->second].get();
2670 break;
2671 case UnitMergeKindReqDoc: {
2672 assert(RuntimeOption::RepoAuthoritative);
2673 void* name = u->lookupLitstrId(it->second);
2674 mi->mergeableObj(ix++) = (char*)name + (int)it->first;
2675 break;
2677 case UnitMergeKindDefine:
2678 case UnitMergeKindGlobal:
2679 assert(RuntimeOption::RepoAuthoritative);
2680 case UnitMergeKindPersistentDefine: {
2681 void* name = u->lookupLitstrId(m_mergeableValues[it->second].first);
2682 mi->mergeableObj(ix++) = (char*)name + (int)it->first;
2683 auto& tv = m_mergeableValues[it->second].second;
2684 auto* tva = (TypedValueAux*)mi->mergeableData(ix);
2685 tva->m_data = tv.m_data;
2686 tva->m_type = tv.m_type;
2687 // leave tva->m_aux uninitialized
2688 ix += sizeof(*tva) / sizeof(void*);
2689 assert(sizeof(*tva) % sizeof(void*) == 0);
2690 break;
2692 case UnitMergeKindDone:
2693 case UnitMergeKindUniqueDefinedClass:
2694 not_reached();
2698 assert(ix == mi->m_mergeablesSize);
2699 mi->mergeableObj(ix) = (void*)UnitMergeKindDone;
2700 u->m_sourceLocTable = createSourceLocTable();
2701 if (m_lineTable.size() == 0) {
2702 u->m_lineTable = createLineTable(m_sourceLocTab, m_bclen);
2703 } else {
2704 u->m_lineTable = m_lineTable;
2706 u->m_lineToOffsetRangeVecMap = createLineToOffsetMap(m_sourceLocTab, m_bclen);
2707 for (size_t i = 0; i < m_feTab.size(); ++i) {
2708 assert(m_feTab[i].second->past() == m_feTab[i].first);
2709 assert(m_fMap.find(m_feTab[i].second) != m_fMap.end());
2710 u->m_funcTable.push_back(
2711 FuncEntry(m_feTab[i].first, m_fMap.find(m_feTab[i].second)->second));
2714 // Funcs can be recorded out of order when loading them from the
2715 // repo currently. So sort 'em here.
2716 std::sort(u->m_funcTable.begin(), u->m_funcTable.end());
2718 m_fMap.clear();
2720 if (RuntimeOption::EvalDumpBytecode) {
2721 // Dump human-readable bytecode.
2722 Trace::traceRelease("%s", u->toString().c_str());
2724 if (RuntimeOption::EvalDumpHhas && SystemLib::s_inited) {
2725 std::printf("%s", disassemble(u).c_str());
2726 std::fflush(stdout);
2727 _Exit(0);
2730 static const bool kVerify = debug || getenv("HHVM_VERIFY");
2731 static const bool kVerifyVerboseSystem =
2732 getenv("HHVM_VERIFY_VERBOSE_SYSTEM");
2733 static const bool kVerifyVerbose =
2734 kVerifyVerboseSystem || getenv("HHVM_VERIFY_VERBOSE");
2736 const bool isSystemLib = u->filepath()->empty() ||
2737 boost::ends_with(u->filepath()->data(), "systemlib.php");
2738 const bool doVerify =
2739 kVerify || boost::ends_with(u->filepath()->data(), "hhas");
2740 if (doVerify) {
2741 Verifier::checkUnit(
2743 isSystemLib ? kVerifyVerboseSystem : kVerifyVerbose
2747 return u;
2750 ///////////////////////////////////////////////////////////////////////////////