2 +----------------------------------------------------------------------+
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-emitter.h"
19 #include "hphp/compiler/option.h"
20 #include "hphp/parser/location.h"
21 #include "hphp/system/systemlib.h"
23 #include "hphp/runtime/base/types.h"
24 #include "hphp/runtime/base/array-data.h"
25 #include "hphp/runtime/base/attr.h"
26 #include "hphp/runtime/base/file-util.h"
27 #include "hphp/runtime/base/runtime-option.h"
28 #include "hphp/runtime/base/static-string-table.h"
29 #include "hphp/runtime/base/typed-value.h"
31 #include "hphp/runtime/ext/std/ext_std_variable.h"
33 #include "hphp/runtime/vm/blob-helper.h"
34 #include "hphp/runtime/vm/disas.h"
35 #include "hphp/runtime/vm/func.h"
36 #include "hphp/runtime/vm/func-emitter.h"
37 #include "hphp/runtime/vm/litstr-table.h"
38 #include "hphp/runtime/vm/native.h"
39 #include "hphp/runtime/vm/preclass.h"
40 #include "hphp/runtime/vm/preclass-emitter.h"
41 #include "hphp/runtime/vm/repo.h"
42 #include "hphp/runtime/vm/repo-helpers.h"
43 #include "hphp/runtime/vm/unit.h"
44 #include "hphp/runtime/vm/verifier/check.h"
46 #include "hphp/util/md5.h"
47 #include "hphp/util/read-only-arena.h"
48 #include "hphp/util/trace.h"
50 #include <boost/algorithm/string/predicate.hpp>
52 #include <folly/Memory.h>
63 ///////////////////////////////////////////////////////////////////////////////
67 using MergeKind
= Unit::MergeKind
;
69 ///////////////////////////////////////////////////////////////////////////////
71 static ReadOnlyArena
& get_readonly_arena() {
72 static ReadOnlyArena
arena(RuntimeOption::EvalHHBCArenaChunkSize
);
77 * Export for the admin server.
79 size_t hhbc_arena_capacity() {
80 if (!RuntimeOption::RepoAuthoritative
) return 0;
81 return get_readonly_arena().capacity();
84 ///////////////////////////////////////////////////////////////////////////////
86 UnitEmitter::UnitEmitter(const MD5
& md5
)
87 : m_mainReturn(make_tv
<KindOfUninit
>())
89 , m_bc((unsigned char*)malloc(BCMaxInit
))
93 , m_allClassesHoistable(true)
96 UnitEmitter::~UnitEmitter() {
99 for (auto& fe
: m_fes
) delete fe
;
100 for (auto& pce
: m_pceVec
) delete pce
;
104 ///////////////////////////////////////////////////////////////////////////////
107 void UnitEmitter::setBc(const unsigned char* bc
, size_t bclen
) {
111 m_bc
= (unsigned char*)malloc(bclen
);
113 memcpy(m_bc
, bc
, bclen
);
118 ///////////////////////////////////////////////////////////////////////////////
119 // Litstrs and Arrays.
121 const StringData
* UnitEmitter::lookupLitstr(Id id
) const {
122 if (isGlobalLitstrId(id
)) {
123 return LitstrTable::get().lookupLitstrId(decodeGlobalLitstrId(id
));
125 assert(id
< m_litstrs
.size());
126 return m_litstrs
[id
];
129 const ArrayData
* UnitEmitter::lookupArray(Id id
) const {
130 assert(id
< m_arrays
.size());
131 return m_arrays
[id
].array
;
134 Id
UnitEmitter::mergeLitstr(const StringData
* litstr
) {
135 if (Option::WholeProgram
) {
136 return encodeGlobalLitstrId(LitstrTable::get().mergeLitstr(litstr
));
138 return mergeUnitLitstr(litstr
);
141 Id
UnitEmitter::mergeUnitLitstr(const StringData
* litstr
) {
142 auto it
= m_litstr2id
.find(litstr
);
143 if (it
== m_litstr2id
.end()) {
144 const StringData
* str
= makeStaticString(litstr
);
145 Id id
= m_litstrs
.size();
146 m_litstrs
.push_back(str
);
147 m_litstr2id
[str
] = id
;
154 Id
UnitEmitter::mergeArray(const ArrayData
* a
) {
155 Variant
v(const_cast<ArrayData
*>(a
));
156 auto key
= HHVM_FN(serialize
)(v
).toCppString();
157 return mergeArray(a
, key
);
160 Id
UnitEmitter::mergeArray(const ArrayData
* a
, const std::string
& key
) {
161 auto const it
= m_array2id
.find(key
);
162 if (it
!= m_array2id
.end()) {
165 a
= ArrayData::GetScalarArray(const_cast<ArrayData
*>(a
), key
);
166 Id id
= m_arrays
.size();
167 ArrayVecElm ave
= {key
, a
};
168 m_arrays
.push_back(ave
);
169 m_array2id
[key
] = id
;
174 ///////////////////////////////////////////////////////////////////////////////
177 void UnitEmitter::initMain(int line1
, int line2
) {
178 assert(m_fes
.size() == 0);
179 StringData
* name
= makeStaticString("");
180 FuncEmitter
* pseudomain
= newFuncEmitter(name
);
181 Attr attrs
= AttrMayUseVV
;
182 pseudomain
->init(line1
, line2
, 0, attrs
, false, name
);
185 void UnitEmitter::addTrivialPseudoMain() {
187 auto const mfe
= getMain();
191 mfe
->maxStackCells
= 1;
192 mfe
->finish(bcPos(), false);
195 TypedValue mainReturn
;
196 mainReturn
.m_data
.num
= 1;
197 mainReturn
.m_type
= KindOfInt64
;
198 m_mainReturn
= mainReturn
;
202 FuncEmitter
* UnitEmitter::newFuncEmitter(const StringData
* name
) {
203 // The pseudomain comes first.
204 assert(m_fes
.size() > 0 || !strcmp(name
->data(), ""));
206 FuncEmitter
* fe
= new FuncEmitter(*this, m_nextFuncSn
++, m_fes
.size(), name
);
211 FuncEmitter
* UnitEmitter::newMethodEmitter(const StringData
* name
,
212 PreClassEmitter
* pce
) {
213 return new FuncEmitter(*this, m_nextFuncSn
++, name
, pce
);
216 void UnitEmitter::appendTopEmitter(FuncEmitter
* fe
) {
217 fe
->setIds(m_nextFuncSn
++, m_fes
.size());
221 void UnitEmitter::recordFunction(FuncEmitter
* fe
) {
222 m_feTab
.push_back(std::make_pair(fe
->past
, fe
));
225 Func
* UnitEmitter::newFunc(const FuncEmitter
* fe
, Unit
& unit
,
226 PreClass
* preClass
, int line1
, int line2
,
227 Offset base
, Offset past
,
228 const StringData
* name
, Attr attrs
, bool top
,
229 const StringData
* docComment
, int numParams
,
230 bool needsNextClonedClosure
) {
231 Func
* f
= new (Func::allocFuncMem(name
, numParams
,
232 needsNextClonedClosure
,
234 Func(unit
, name
, attrs
);
240 ///////////////////////////////////////////////////////////////////////////////
243 PreClassEmitter
* UnitEmitter::newPreClassEmitter(
244 const StringData
* name
,
245 PreClass::Hoistable hoistable
247 if (hoistable
&& m_hoistablePreClassSet
.count(name
)) {
248 hoistable
= PreClass::Mergeable
;
251 PreClassEmitter
* pce
= new PreClassEmitter(*this, m_pceVec
.size(),
254 if (hoistable
>= PreClass::MaybeHoistable
) {
255 m_hoistablePreClassSet
.insert(name
);
256 if (hoistable
== PreClass::ClosureHoistable
) {
257 // Closures should appear at the VERY top of the file, so if any class in
258 // the same file tries to use them, they are already defined. We had a
259 // fun race where one thread was autoloading a file, finished parsing the
260 // class, then another thread came along and saw the class was already
261 // loaded and ran it before the first thread had time to parse the
263 m_hoistablePceIdList
.push_front(pce
->id());
265 m_hoistablePceIdList
.push_back(pce
->id());
268 m_allClassesHoistable
= false;
270 if (hoistable
>= PreClass::Mergeable
&&
271 hoistable
< PreClass::AlwaysHoistable
) {
273 m_allClassesHoistable
= false;
275 pushMergeableClass(pce
);
278 m_pceVec
.push_back(pce
);
283 ///////////////////////////////////////////////////////////////////////////////
286 Id
UnitEmitter::addTypeAlias(const TypeAlias
& td
) {
287 Id id
= m_typeAliases
.size();
288 m_typeAliases
.push_back(td
);
293 ///////////////////////////////////////////////////////////////////////////////
296 SourceLocTable
UnitEmitter::createSourceLocTable() const {
297 SourceLocTable locations
;
298 for (size_t i
= 0; i
< m_sourceLocTab
.size(); ++i
) {
299 Offset endOff
= i
< m_sourceLocTab
.size() - 1
300 ? m_sourceLocTab
[i
+ 1].first
302 locations
.push_back(SourceLocEntry(endOff
, m_sourceLocTab
[i
].second
));
309 using SrcLoc
= std::vector
<std::pair
<Offset
, SourceLoc
>>;
312 * Create a LineTable from `srcLoc'.
314 LineTable
createLineTable(SrcLoc
& srcLoc
, Offset bclen
) {
316 for (size_t i
= 0; i
< srcLoc
.size(); ++i
) {
317 Offset endOff
= i
< srcLoc
.size() - 1 ? srcLoc
[i
+ 1].first
: bclen
;
318 lines
.push_back(LineEntry(endOff
, srcLoc
[i
].second
.line1
));
325 void UnitEmitter::recordSourceLocation(const Location
* sLoc
, Offset start
) {
326 // Some byte codes, such as for the implicit "return 0" at the end of a
327 // a source file do not have valid source locations. This check makes
328 // sure we don't record a (dummy) source location in this case.
329 if (start
> 0 && sLoc
->line0
== 1 && sLoc
->char0
== 1 &&
330 sLoc
->line1
== 1 && sLoc
->char1
== 1 && strlen(sLoc
->file
) == 0) return;
331 SourceLoc
newLoc(*sLoc
);
332 if (!m_sourceLocTab
.empty()) {
333 if (m_sourceLocTab
.back().second
== newLoc
) {
334 // Combine into the interval already at the back of the vector.
335 assert(start
>= m_sourceLocTab
.back().first
);
338 assert(m_sourceLocTab
.back().first
< start
&&
339 "source location offsets must be added to UnitEmitter in "
342 // First record added should be for bytecode offset zero.
345 m_sourceLocTab
.push_back(std::make_pair(start
, newLoc
));
349 ///////////////////////////////////////////////////////////////////////////////
352 void UnitEmitter::pushMergeableClass(PreClassEmitter
* e
) {
353 m_mergeableStmts
.push_back(std::make_pair(MergeKind::Class
, e
->id()));
356 void UnitEmitter::pushMergeableInclude(Unit::MergeKind kind
,
357 const StringData
* unitName
) {
358 m_mergeableStmts
.push_back(
359 std::make_pair(kind
, mergeLitstr(unitName
)));
360 m_allClassesHoistable
= false;
363 void UnitEmitter::insertMergeableInclude(int ix
, Unit::MergeKind kind
, Id id
) {
364 assert(size_t(ix
) <= m_mergeableStmts
.size());
365 m_mergeableStmts
.insert(m_mergeableStmts
.begin() + ix
,
366 std::make_pair(kind
, id
));
367 m_allClassesHoistable
= false;
370 void UnitEmitter::pushMergeableDef(Unit::MergeKind kind
,
371 const StringData
* name
,
372 const TypedValue
& tv
) {
373 m_mergeableStmts
.push_back(std::make_pair(kind
, m_mergeableValues
.size()));
374 m_mergeableValues
.push_back(std::make_pair(mergeLitstr(name
), tv
));
375 m_allClassesHoistable
= false;
378 void UnitEmitter::insertMergeableDef(int ix
, Unit::MergeKind kind
,
379 Id id
, const TypedValue
& tv
) {
380 assert(size_t(ix
) <= m_mergeableStmts
.size());
381 m_mergeableStmts
.insert(m_mergeableStmts
.begin() + ix
,
382 std::make_pair(kind
, m_mergeableValues
.size()));
383 m_mergeableValues
.push_back(std::make_pair(id
, tv
));
384 m_allClassesHoistable
= false;
388 ///////////////////////////////////////////////////////////////////////////////
389 // Initialization and execution.
391 void UnitEmitter::commit(UnitOrigin unitOrigin
) {
392 Repo
& repo
= Repo::get();
395 bool err
= insert(unitOrigin
, txn
);
399 } catch (RepoExc
& re
) {
400 int repoId
= repo
.repoIdForNewUnit(unitOrigin
);
401 if (repoId
!= RepoIdInvalid
) {
402 TRACE(3, "Failed to commit '%s' (0x%016" PRIx64
"%016" PRIx64
") to '%s': %s\n",
403 m_filepath
->data(), m_md5
.q
[0], m_md5
.q
[1],
404 repo
.repoName(repoId
).c_str(), re
.msg().c_str());
409 bool UnitEmitter::insert(UnitOrigin unitOrigin
, RepoTxn
& txn
) {
410 Repo
& repo
= Repo::get();
411 UnitRepoProxy
& urp
= repo
.urp();
412 int repoId
= Repo::get().repoIdForNewUnit(unitOrigin
);
413 if (repoId
== RepoIdInvalid
) {
420 m_lineTable
= createLineTable(m_sourceLocTab
, m_bclen
);
421 urp
.insertUnit
[repoId
].insert(*this, txn
, m_sn
, m_md5
, m_bc
,
425 urp
.insertUnitLineTable(repoId
, txn
, usn
, m_lineTable
);
426 for (unsigned i
= 0; i
< m_litstrs
.size(); ++i
) {
427 urp
.insertUnitLitstr
[repoId
].insert(txn
, usn
, i
, m_litstrs
[i
]);
429 for (unsigned i
= 0; i
< m_arrays
.size(); ++i
) {
430 urp
.insertUnitArray
[repoId
].insert(txn
, usn
, i
,
431 m_arrays
[i
].serialized
);
433 for (auto& fe
: m_fes
) {
436 for (auto& pce
: m_pceVec
) {
440 for (int i
= 0, n
= m_mergeableStmts
.size(); i
< n
; i
++) {
441 switch (m_mergeableStmts
[i
].first
) {
442 case MergeKind::Done
:
443 case MergeKind::UniqueDefinedClass
:
445 case MergeKind::Class
: break;
446 case MergeKind::ReqDoc
: {
447 urp
.insertUnitMergeable
[repoId
].insert(
449 m_mergeableStmts
[i
].first
, m_mergeableStmts
[i
].second
, nullptr);
452 case MergeKind::Define
:
453 case MergeKind::PersistentDefine
:
454 case MergeKind::Global
: {
455 int ix
= m_mergeableStmts
[i
].second
;
456 urp
.insertUnitMergeable
[repoId
].insert(
458 m_mergeableStmts
[i
].first
,
459 m_mergeableValues
[ix
].first
, &m_mergeableValues
[ix
].second
);
464 if (RuntimeOption::RepoDebugInfo
) {
465 for (size_t i
= 0; i
< m_sourceLocTab
.size(); ++i
) {
466 SourceLoc
& e
= m_sourceLocTab
[i
].second
;
467 Offset endOff
= i
< m_sourceLocTab
.size() - 1
468 ? m_sourceLocTab
[i
+ 1].first
471 urp
.insertUnitSourceLoc
[repoId
]
472 .insert(txn
, usn
, endOff
, e
.line0
, e
.char0
, e
.line1
, e
.char1
);
476 } catch (RepoExc
& re
) {
477 TRACE(3, "Failed to commit '%s' (0x%016" PRIx64
"%016" PRIx64
") to '%s': %s\n",
478 m_filepath
->data(), m_md5
.q
[0], m_md5
.q
[1],
479 repo
.repoName(repoId
).c_str(), re
.msg().c_str());
484 static const unsigned char*
485 allocateBCRegion(const unsigned char* bc
, size_t bclen
) {
486 if (RuntimeOption::RepoAuthoritative
) {
487 // In RepoAuthoritative, we assume we won't ever deallocate units
488 // and that this is read-only, mostly cold data. So we throw it
489 // in a bump-allocator that's mprotect'd to prevent writes.
490 return static_cast<const unsigned char*>(
491 get_readonly_arena().allocate(bc
, bclen
)
494 auto mem
= static_cast<unsigned char*>(malloc(bclen
));
495 std::copy(bc
, bc
+ bclen
, mem
);
499 std::unique_ptr
<Unit
> UnitEmitter::create() {
500 auto u
= folly::make_unique
<Unit
>();
501 u
->m_repoId
= m_repoId
;
503 u
->m_bc
= allocateBCRegion(m_bc
, m_bclen
);
504 u
->m_bclen
= m_bclen
;
505 u
->m_filepath
= m_filepath
;
506 u
->m_mainReturn
= m_mainReturn
;
507 u
->m_mergeOnly
= m_mergeOnly
;
508 u
->m_isHHFile
= m_isHHFile
;
510 const std::string
& dirname
= FileUtil::safe_dirname(m_filepath
->data(),
512 u
->m_dirpath
= makeStaticString(dirname
);
515 for (unsigned i
= 0; i
< m_litstrs
.size(); ++i
) {
517 np
.first
= m_litstrs
[i
];
519 u
->m_namedInfo
.push_back(np
);
521 u
->m_arrays
= [&]() -> std::vector
<const ArrayData
*> {
522 auto ret
= std::vector
<const ArrayData
*>{};
523 for (unsigned i
= 0; i
< m_arrays
.size(); ++i
) {
524 ret
.push_back(m_arrays
[i
].array
);
528 for (auto const& pce
: m_pceVec
) {
529 u
->m_preClasses
.push_back(PreClassPtr(pce
->create(*u
)));
531 u
->m_typeAliases
= m_typeAliases
;
533 size_t ix
= m_fes
.size() + m_hoistablePceIdList
.size();
534 if (m_mergeOnly
&& !m_allClassesHoistable
) {
536 for (auto& mergeable
: m_mergeableStmts
) {
538 if (!RuntimeOption::RepoAuthoritative
&& SystemLib::s_inited
) {
539 if (mergeable
.first
!= MergeKind::Class
) {
541 u
->m_mergeOnly
= false;
545 switch (mergeable
.first
) {
546 case MergeKind::PersistentDefine
:
547 case MergeKind::Define
:
548 case MergeKind::Global
:
549 extra
+= sizeof(TypedValueAux
) / sizeof(void*);
558 Unit::MergeInfo
*mi
= Unit::MergeInfo::alloc(ix
);
561 for (auto& fe
: m_fes
) {
562 Func
* func
= fe
->create(*u
);
564 if (!mi
->m_firstHoistableFunc
) {
565 mi
->m_firstHoistableFunc
= ix
;
568 assert(!mi
->m_firstHoistableFunc
);
570 mi
->mergeableObj(ix
++) = func
;
572 assert(u
->getMain()->isPseudoMain());
573 if (!mi
->m_firstHoistableFunc
) {
574 mi
->m_firstHoistableFunc
= ix
;
576 mi
->m_firstHoistablePreClass
= ix
;
577 assert(m_fes
.size());
578 for (auto& id
: m_hoistablePceIdList
) {
579 mi
->mergeableObj(ix
++) = u
->m_preClasses
[id
].get();
581 mi
->m_firstMergeablePreClass
= ix
;
582 if (u
->m_mergeOnly
&& !m_allClassesHoistable
) {
583 for (auto& mergeable
: m_mergeableStmts
) {
584 switch (mergeable
.first
) {
585 case MergeKind::Class
:
586 mi
->mergeableObj(ix
++) = u
->m_preClasses
[mergeable
.second
].get();
588 case MergeKind::ReqDoc
: {
589 assert(RuntimeOption::RepoAuthoritative
);
590 void* name
= u
->lookupLitstrId(mergeable
.second
);
591 mi
->mergeableObj(ix
++) = (char*)name
+ (int)mergeable
.first
;
594 case MergeKind::Define
:
595 case MergeKind::Global
:
596 assert(RuntimeOption::RepoAuthoritative
);
597 case MergeKind::PersistentDefine
: {
598 void* name
= u
->lookupLitstrId
599 (m_mergeableValues
[mergeable
.second
].first
);
600 mi
->mergeableObj(ix
++) = (char*)name
+ (int)mergeable
.first
;
601 auto& tv
= m_mergeableValues
[mergeable
.second
].second
;
602 auto* tva
= (TypedValueAux
*)mi
->mergeableData(ix
);
603 tva
->m_data
= tv
.m_data
;
604 tva
->m_type
= tv
.m_type
;
605 // leave tva->m_aux uninitialized
606 ix
+= sizeof(*tva
) / sizeof(void*);
607 assert(sizeof(*tva
) % sizeof(void*) == 0);
610 case MergeKind::Done
:
611 case MergeKind::UniqueDefinedClass
:
616 assert(ix
== mi
->m_mergeablesSize
);
617 mi
->mergeableObj(ix
) = (void*)MergeKind::Done
;
620 * What's going on is we're going to have a m_lineTable if this UnitEmitter
621 * was loaded from the repo, and no m_sourceLocTab (it's demand-loaded by
622 * unit.cpp because it's only used for the debugger). Don't bother creating
623 * the line table here, because we can retrieve it from the repo later.
625 * On the other hand, if this unit was just created by parsing a php file (or
626 * whatnot) which was not committed to the repo, we'll have a m_sourceLocTab.
627 * In this case we should populate m_lineTable (otherwise we might lose line
628 * info altogether, since it may not be backed by a repo).
630 if (m_sourceLocTab
.size() != 0) {
631 stashLineTable(u
.get(), createLineTable(m_sourceLocTab
, m_bclen
));
634 for (size_t i
= 0; i
< m_feTab
.size(); ++i
) {
635 assert(m_feTab
[i
].second
->past
== m_feTab
[i
].first
);
636 assert(m_fMap
.find(m_feTab
[i
].second
) != m_fMap
.end());
637 u
->m_funcTable
.push_back(
638 FuncEntry(m_feTab
[i
].first
, m_fMap
.find(m_feTab
[i
].second
)->second
));
641 // Funcs can be recorded out of order when loading them from the
642 // repo currently. So sort 'em here.
643 std::sort(u
->m_funcTable
.begin(), u
->m_funcTable
.end());
647 if (RuntimeOption::EvalDumpBytecode
) {
648 // Dump human-readable bytecode.
649 Trace::traceRelease("%s", u
->toString().c_str());
651 if (RuntimeOption::EvalDumpHhas
&& SystemLib::s_inited
) {
652 std::printf("%s", disassemble(u
.get()).c_str());
657 static const bool kVerify
= debug
|| getenv("HHVM_VERIFY");
658 static const bool kVerifyVerboseSystem
=
659 getenv("HHVM_VERIFY_VERBOSE_SYSTEM");
660 static const bool kVerifyVerbose
=
661 kVerifyVerboseSystem
|| getenv("HHVM_VERIFY_VERBOSE");
663 const bool isSystemLib
= u
->filepath()->empty() ||
664 boost::ends_with(u
->filepath()->data(), "systemlib.php");
665 const bool doVerify
=
666 kVerify
|| boost::ends_with(u
->filepath()->data(), "hhas");
670 isSystemLib
? kVerifyVerboseSystem
: kVerifyVerbose
677 template<class SerDe
>
678 void UnitEmitter::serdeMetaData(SerDe
& sd
) {
688 ///////////////////////////////////////////////////////////////////////////////
691 UnitRepoProxy::UnitRepoProxy(Repo
& repo
)
693 #define URP_OP(c, o) \
694 , o{c##Stmt(repo, 0), c##Stmt(repo, 1)}
699 UnitRepoProxy::~UnitRepoProxy() {
702 void UnitRepoProxy::createSchema(int repoId
, RepoTxn
& txn
) {
704 std::stringstream ssCreate
;
705 ssCreate
<< "CREATE TABLE " << m_repo
.table(repoId
, "Unit")
706 << "(unitSn INTEGER PRIMARY KEY, md5 BLOB, bc BLOB, data BLOB, "
708 txn
.exec(ssCreate
.str());
711 std::stringstream ssCreate
;
712 ssCreate
<< "CREATE TABLE " << m_repo
.table(repoId
, "UnitLitstr")
713 << "(unitSn INTEGER, litstrId INTEGER, litstr TEXT,"
714 " PRIMARY KEY (unitSn, litstrId));";
715 txn
.exec(ssCreate
.str());
718 std::stringstream ssCreate
;
719 ssCreate
<< "CREATE TABLE " << m_repo
.table(repoId
, "UnitArray")
720 << "(unitSn INTEGER, arrayId INTEGER, array BLOB,"
721 " PRIMARY KEY (unitSn, arrayId));";
722 txn
.exec(ssCreate
.str());
725 std::stringstream ssCreate
;
726 ssCreate
<< "CREATE TABLE " << m_repo
.table(repoId
, "UnitMergeables")
727 << "(unitSn INTEGER, mergeableIx INTEGER,"
728 " mergeableKind INTEGER, mergeableId INTEGER,"
729 " mergeableValue BLOB,"
730 " PRIMARY KEY (unitSn, mergeableIx));";
731 txn
.exec(ssCreate
.str());
734 std::stringstream ssCreate
;
735 ssCreate
<< "CREATE TABLE " << m_repo
.table(repoId
, "UnitSourceLoc")
736 << "(unitSn INTEGER, pastOffset INTEGER, line0 INTEGER,"
737 " char0 INTEGER, line1 INTEGER, char1 INTEGER,"
738 " PRIMARY KEY (unitSn, pastOffset));";
739 txn
.exec(ssCreate
.str());
742 std::stringstream ssCreate
;
743 ssCreate
<< "CREATE TABLE " << m_repo
.table(repoId
, "UnitLineTable")
744 << "(unitSn INTEGER PRIMARY KEY, data BLOB);";
745 txn
.exec(ssCreate
.str());
749 bool UnitRepoProxy::loadHelper(UnitEmitter
& ue
,
750 const std::string
& name
,
752 ue
.m_filepath
= makeStaticString(name
);
753 // Look for a repo that contains a unit with matching MD5.
755 for (repoId
= RepoIdCount
- 1; repoId
>= 0; --repoId
) {
756 if (!getUnit
[repoId
].get(ue
, md5
)) {
761 TRACE(3, "No repo contains '%s' (0x%016" PRIx64
"%016" PRIx64
")\n",
762 name
.c_str(), md5
.q
[0], md5
.q
[1]);
766 getUnitLitstrs
[repoId
].get(ue
);
767 getUnitArrays
[repoId
].get(ue
);
768 m_repo
.pcrp().getPreClasses
[repoId
].get(ue
);
769 getUnitMergeables
[repoId
].get(ue
);
770 getUnitLineTable(repoId
, ue
.m_sn
, ue
.m_lineTable
);
771 m_repo
.frp().getFuncs
[repoId
].get(ue
);
772 } catch (RepoExc
& re
) {
774 "Repo error loading '%s' (0x%016" PRIx64
"%016"
775 PRIx64
") from '%s': %s\n",
776 name
.c_str(), md5
.q
[0], md5
.q
[1], m_repo
.repoName(repoId
).c_str(),
780 TRACE(3, "Repo loaded '%s' (0x%016" PRIx64
"%016" PRIx64
") from '%s'\n",
781 name
.c_str(), md5
.q
[0], md5
.q
[1], m_repo
.repoName(repoId
).c_str());
785 std::unique_ptr
<UnitEmitter
>
786 UnitRepoProxy::loadEmitter(const std::string
& name
, const MD5
& md5
) {
787 auto ue
= folly::make_unique
<UnitEmitter
>(md5
);
788 if (!loadHelper(*ue
, name
, md5
)) ue
.reset();
792 std::unique_ptr
<Unit
>
793 UnitRepoProxy::load(const std::string
& name
, const MD5
& md5
) {
795 if (!loadHelper(ue
, name
, md5
)) return nullptr;
799 void UnitRepoProxy::InsertUnitStmt
800 ::insert(const UnitEmitter
& ue
,
801 RepoTxn
& txn
, int64_t& unitSn
, const MD5
& md5
,
802 const unsigned char* bc
, size_t bclen
) {
803 BlobEncoder dataBlob
;
806 std::stringstream ssInsert
;
807 ssInsert
<< "INSERT INTO " << m_repo
.table(m_repoId
, "Unit")
808 << " VALUES(NULL, @md5, @bc, @data);";
809 txn
.prepare(*this, ssInsert
.str());
811 RepoTxnQuery
query(txn
, *this);
812 query
.bindMd5("@md5", md5
);
813 query
.bindBlob("@bc", (const void*)bc
, bclen
);
814 const_cast<UnitEmitter
&>(ue
).serdeMetaData(dataBlob
);
815 query
.bindBlob("@data", dataBlob
, /* static */ true);
817 unitSn
= query
.getInsertedRowid();
820 bool UnitRepoProxy::GetUnitStmt
821 ::get(UnitEmitter
& ue
, const MD5
& md5
) {
825 std::stringstream ssSelect
;
826 ssSelect
<< "SELECT unitSn,bc,data FROM "
827 << m_repo
.table(m_repoId
, "Unit")
828 << " WHERE md5 == @md5;";
829 txn
.prepare(*this, ssSelect
.str());
831 RepoTxnQuery
query(txn
, *this);
832 query
.bindMd5("@md5", md5
);
837 int64_t unitSn
; /**/ query
.getInt64(0, unitSn
);
838 const void* bc
; size_t bclen
; /**/ query
.getBlob(1, bc
, bclen
);
839 BlobDecoder dataBlob
= /**/ query
.getBlob(2);
841 ue
.m_repoId
= m_repoId
;
843 ue
.setBc(static_cast<const unsigned char*>(bc
), bclen
);
844 ue
.serdeMetaData(dataBlob
);
847 } catch (RepoExc
& re
) {
853 void UnitRepoProxy::InsertUnitLitstrStmt
854 ::insert(RepoTxn
& txn
, int64_t unitSn
, Id litstrId
,
855 const StringData
* litstr
) {
857 std::stringstream ssInsert
;
858 ssInsert
<< "INSERT INTO " << m_repo
.table(m_repoId
, "UnitLitstr")
859 << " VALUES(@unitSn, @litstrId, @litstr);";
860 txn
.prepare(*this, ssInsert
.str());
862 RepoTxnQuery
query(txn
, *this);
863 query
.bindInt64("@unitSn", unitSn
);
864 query
.bindId("@litstrId", litstrId
);
865 query
.bindStaticString("@litstr", litstr
);
869 void UnitRepoProxy::GetUnitLitstrsStmt
870 ::get(UnitEmitter
& ue
) {
873 std::stringstream ssSelect
;
874 ssSelect
<< "SELECT litstrId,litstr FROM "
875 << m_repo
.table(m_repoId
, "UnitLitstr")
876 << " WHERE unitSn == @unitSn ORDER BY litstrId ASC;";
877 txn
.prepare(*this, ssSelect
.str());
879 RepoTxnQuery
query(txn
, *this);
880 query
.bindInt64("@unitSn", ue
.m_sn
);
884 Id litstrId
; /**/ query
.getId(0, litstrId
);
885 StringData
* litstr
; /**/ query
.getStaticString(1, litstr
);
886 Id id UNUSED
= ue
.mergeUnitLitstr(litstr
);
887 assert(id
== litstrId
);
889 } while (!query
.done());
893 void UnitRepoProxy::InsertUnitArrayStmt
894 ::insert(RepoTxn
& txn
, int64_t unitSn
, Id arrayId
,
895 const std::string
& array
) {
897 std::stringstream ssInsert
;
898 ssInsert
<< "INSERT INTO " << m_repo
.table(m_repoId
, "UnitArray")
899 << " VALUES(@unitSn, @arrayId, @array);";
900 txn
.prepare(*this, ssInsert
.str());
902 RepoTxnQuery
query(txn
, *this);
903 query
.bindInt64("@unitSn", unitSn
);
904 query
.bindId("@arrayId", arrayId
);
905 query
.bindStdString("@array", array
);
909 void UnitRepoProxy::GetUnitArraysStmt
910 ::get(UnitEmitter
& ue
) {
913 std::stringstream ssSelect
;
914 ssSelect
<< "SELECT arrayId,array FROM "
915 << m_repo
.table(m_repoId
, "UnitArray")
916 << " WHERE unitSn == @unitSn ORDER BY arrayId ASC;";
917 txn
.prepare(*this, ssSelect
.str());
919 RepoTxnQuery
query(txn
, *this);
920 query
.bindInt64("@unitSn", ue
.m_sn
);
924 Id arrayId
; /**/ query
.getId(0, arrayId
);
925 std::string key
; /**/ query
.getStdString(1, key
);
926 Variant v
= unserialize_from_buffer(key
.data(), key
.size());
927 Id id UNUSED
= ue
.mergeArray(v
.asArrRef().get(), key
);
928 assert(id
== arrayId
);
930 } while (!query
.done());
934 void UnitRepoProxy::InsertUnitMergeableStmt
935 ::insert(RepoTxn
& txn
, int64_t unitSn
,
936 int ix
, Unit::MergeKind kind
, Id id
,
939 std::stringstream ssInsert
;
940 ssInsert
<< "INSERT INTO " << m_repo
.table(m_repoId
, "UnitMergeables")
941 << " VALUES(@unitSn, @mergeableIx, @mergeableKind,"
942 " @mergeableId, @mergeableValue);";
943 txn
.prepare(*this, ssInsert
.str());
946 RepoTxnQuery
query(txn
, *this);
947 query
.bindInt64("@unitSn", unitSn
);
948 query
.bindInt("@mergeableIx", ix
);
949 query
.bindInt("@mergeableKind", (int)kind
);
950 query
.bindId("@mergeableId", id
);
952 assert(kind
== MergeKind::Define
||
953 kind
== MergeKind::PersistentDefine
||
954 kind
== MergeKind::Global
);
955 query
.bindTypedValue("@mergeableValue", *value
);
957 assert(kind
== MergeKind::ReqDoc
);
958 query
.bindNull("@mergeableValue");
963 void UnitRepoProxy::GetUnitMergeablesStmt
964 ::get(UnitEmitter
& ue
) {
967 std::stringstream ssSelect
;
968 ssSelect
<< "SELECT mergeableIx,mergeableKind,mergeableId,mergeableValue"
970 << m_repo
.table(m_repoId
, "UnitMergeables")
971 << " WHERE unitSn == @unitSn ORDER BY mergeableIx ASC;";
972 txn
.prepare(*this, ssSelect
.str());
974 RepoTxnQuery
query(txn
, *this);
975 query
.bindInt64("@unitSn", ue
.m_sn
);
979 int mergeableIx
; /**/ query
.getInt(0, mergeableIx
);
980 int mergeableKind
; /**/ query
.getInt(1, mergeableKind
);
981 Id mergeableId
; /**/ query
.getInt(2, mergeableId
);
983 auto k
= MergeKind(mergeableKind
);
985 if (UNLIKELY(!RuntimeOption::RepoAuthoritative
)) {
987 * We're using a repo generated in WholeProgram mode,
988 * but we're not using it in RepoAuthoritative mode
989 * (this is dodgy to start with). We're not going to
990 * deal with requires at merge time, so drop them
991 * here, and clear the mergeOnly flag for the unit.
992 * The one exception is persistent constants are allowed in systemlib.
994 if (k
!= MergeKind::PersistentDefine
|| SystemLib::s_inited
) {
995 ue
.m_mergeOnly
= false;
999 case MergeKind::ReqDoc
:
1000 ue
.insertMergeableInclude(mergeableIx
, k
, mergeableId
);
1002 case MergeKind::PersistentDefine
:
1003 case MergeKind::Define
:
1004 case MergeKind::Global
: {
1005 TypedValue mergeableValue
; /**/ query
.getTypedValue(3,
1007 ue
.insertMergeableDef(mergeableIx
, k
, mergeableId
, mergeableValue
);
1013 } while (!query
.done());
1017 void UnitRepoProxy::insertUnitLineTable(int repoId
,
1020 LineTable
& lineTable
) {
1021 RepoStmt
stmt(m_repo
);
1024 "INSERT INTO {} VALUES(@unitSn, @data);",
1025 m_repo
.table(repoId
, "UnitLineTable")
1028 RepoTxnQuery
query(txn
, stmt
);
1029 BlobEncoder dataBlob
;
1030 dataBlob
.encode(lineTable
);
1031 query
.bindInt64("@unitSn", unitSn
);
1032 query
.bindBlob("@data", dataBlob
, /* static */ true);
1036 void UnitRepoProxy::getUnitLineTable(int repoId
,
1038 LineTable
& lineTable
) {
1039 RepoStmt
stmt(m_repo
);
1042 "SELECT data FROM {} WHERE unitSn == @unitSn;",
1043 m_repo
.table(repoId
, "UnitLineTable")
1046 RepoTxn
txn(m_repo
);
1047 RepoTxnQuery
query(txn
, stmt
);
1048 query
.bindInt64("@unitSn", unitSn
);
1051 BlobDecoder dataBlob
= query
.getBlob(0);
1052 dataBlob
.decode(lineTable
);
1057 void UnitRepoProxy::InsertUnitSourceLocStmt
1058 ::insert(RepoTxn
& txn
, int64_t unitSn
, Offset pastOffset
,
1059 int line0
, int char0
, int line1
, int char1
) {
1061 std::stringstream ssInsert
;
1062 ssInsert
<< "INSERT INTO " << m_repo
.table(m_repoId
, "UnitSourceLoc")
1063 << " VALUES(@unitSn, @pastOffset, @line0, @char0, @line1,"
1065 txn
.prepare(*this, ssInsert
.str());
1067 RepoTxnQuery
query(txn
, *this);
1068 query
.bindInt64("@unitSn", unitSn
);
1069 query
.bindOffset("@pastOffset", pastOffset
);
1070 query
.bindInt("@line0", line0
);
1071 query
.bindInt("@char0", char0
);
1072 query
.bindInt("@line1", line1
);
1073 query
.bindInt("@char1", char1
);
1077 bool UnitRepoProxy::GetSourceLocTabStmt
1078 ::get(int64_t unitSn
, SourceLocTable
& sourceLocTab
) {
1080 RepoTxn
txn(m_repo
);
1082 std::stringstream ssSelect
;
1083 ssSelect
<< "SELECT pastOffset,line0,char0,line1,char1 FROM "
1084 << m_repo
.table(m_repoId
, "UnitSourceLoc")
1085 << " WHERE unitSn == @unitSn"
1086 " ORDER BY pastOffset ASC;";
1087 txn
.prepare(*this, ssSelect
.str());
1089 RepoTxnQuery
query(txn
, *this);
1090 query
.bindInt64("@unitSn", unitSn
);
1097 query
.getOffset(0, pastOffset
);
1099 query
.getInt(1, sLoc
.line0
);
1100 query
.getInt(2, sLoc
.char0
);
1101 query
.getInt(3, sLoc
.line1
);
1102 query
.getInt(4, sLoc
.char1
);
1103 SourceLocEntry
entry(pastOffset
, sLoc
);
1104 sourceLocTab
.push_back(entry
);
1105 } while (!query
.done());
1107 } catch (RepoExc
& re
) {
1113 ///////////////////////////////////////////////////////////////////////////////