Use RATs from HHBBC in init_use_vars
[hiphop-php.git] / hphp / runtime / vm / func-emitter.cpp
blobc6ab768111c35f7f6c1ed68fcfeac8772f3d78ad
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2015 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/func-emitter.h"
19 #include "hphp/parser/parser.h"
21 #include "hphp/runtime/ext/extension.h"
22 #include "hphp/runtime/base/unit-cache.h"
23 #include "hphp/runtime/base/rds.h"
24 #include "hphp/runtime/base/strings.h"
26 #include "hphp/runtime/vm/blob-helper.h"
27 #include "hphp/runtime/vm/bytecode.h"
28 #include "hphp/runtime/vm/func-inline.h"
29 #include "hphp/runtime/vm/native.h"
30 #include "hphp/runtime/vm/repo.h"
31 #include "hphp/runtime/vm/runtime.h"
33 #include "hphp/runtime/vm/jit/mc-generator.h"
34 #include "hphp/runtime/vm/jit/types.h"
36 #include "hphp/runtime/vm/verifier/cfg.h"
38 #include "hphp/system/systemlib.h"
40 #include "hphp/util/atomic-vector.h"
41 #include "hphp/util/debug.h"
42 #include "hphp/util/trace.h"
43 #include "hphp/runtime/ext_zend_compat/hhvm/zend-wrap-func.h"
45 namespace HPHP {
46 ///////////////////////////////////////////////////////////////////////////////
47 // FuncEmitter.
49 FuncEmitter::FuncEmitter(UnitEmitter& ue, int sn, Id id, const StringData* n)
50 : m_ue(ue)
51 , m_pce(nullptr)
52 , m_sn(sn)
53 , m_id(id)
54 , name(n)
55 , top(false)
56 , returnType(folly::none)
57 , retUserType(nullptr)
58 , isClosureBody(false)
59 , isAsync(false)
60 , isGenerator(false)
61 , isPairGenerator(false)
62 , isMemoizeImpl(false)
63 , isMemoizeWrapper(false)
64 , hasMemoizeSharedProp(false)
65 , containsCalls(false)
66 , docComment(nullptr)
67 , originalFilename(nullptr)
68 , memoizePropName(nullptr)
69 , memoizeSharedPropIndex(0)
70 , m_numLocals(0)
71 , m_numUnnamedLocals(0)
72 , m_activeUnnamedLocals(0)
73 , m_numIterators(0)
74 , m_nextFreeIterator(0)
75 , m_info(nullptr)
76 , m_builtinFuncPtr(nullptr)
77 , m_nativeFuncPtr(nullptr)
78 , m_ehTabSorted(false)
81 FuncEmitter::FuncEmitter(UnitEmitter& ue, int sn, const StringData* n,
82 PreClassEmitter* pce)
83 : m_ue(ue)
84 , m_pce(pce)
85 , m_sn(sn)
86 , m_id(kInvalidId)
87 , name(n)
88 , top(false)
89 , returnType(folly::none)
90 , retUserType(nullptr)
91 , isClosureBody(false)
92 , isAsync(false)
93 , isGenerator(false)
94 , isPairGenerator(false)
95 , isMemoizeImpl(false)
96 , isMemoizeWrapper(false)
97 , hasMemoizeSharedProp(false)
98 , containsCalls(false)
99 , docComment(nullptr)
100 , originalFilename(nullptr)
101 , memoizePropName(nullptr)
102 , memoizeSharedPropIndex(0)
103 , m_numLocals(0)
104 , m_numUnnamedLocals(0)
105 , m_activeUnnamedLocals(0)
106 , m_numIterators(0)
107 , m_nextFreeIterator(0)
108 , m_info(nullptr)
109 , m_builtinFuncPtr(nullptr)
110 , m_nativeFuncPtr(nullptr)
111 , m_ehTabSorted(false)
114 FuncEmitter::~FuncEmitter() {
118 ///////////////////////////////////////////////////////////////////////////////
119 // Initialization and execution.
121 void FuncEmitter::init(int l1, int l2, Offset base_, Attr attrs_, bool top_,
122 const StringData* docComment_) {
123 base = base_;
124 line1 = l1;
125 line2 = l2;
126 top = top_;
127 attrs = attrs_;
128 docComment = docComment_;
130 if (!isPseudoMain()) {
131 if (!SystemLib::s_inited) {
132 assert(attrs & AttrBuiltin);
134 if ((attrs & AttrBuiltin) && !pce()) {
135 attrs |= AttrSkipFrame;
140 void FuncEmitter::finish(Offset past_, bool load) {
141 past = past_;
142 sortEHTab();
143 sortFPITab(load);
146 void FuncEmitter::commit(RepoTxn& txn) const {
147 Repo& repo = Repo::get();
148 FuncRepoProxy& frp = repo.frp();
149 int repoId = m_ue.m_repoId;
150 int64_t usn = m_ue.m_sn;
152 frp.insertFunc[repoId]
153 .insert(*this, txn, usn, m_sn, m_pce ? m_pce->id() : -1, name, top);
156 static std::vector<EHEnt> toFixed(const std::vector<EHEntEmitter>& vec) {
157 std::vector<EHEnt> ret;
158 for (auto const& ehe : vec) {
159 EHEnt e;
160 e.m_type = ehe.m_type;
161 e.m_itRef = ehe.m_itRef;
162 e.m_base = ehe.m_base;
163 e.m_past = ehe.m_past;
164 e.m_iterId = ehe.m_iterId;
165 e.m_parentIndex = ehe.m_parentIndex;
166 e.m_fault = ehe.m_fault;
167 e.m_catches = ehe.m_catches;
168 ret.emplace_back(std::move(e));
170 return ret;
173 Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
174 bool isGenerated = isdigit(name->data()[0]) ||
175 ParserBase::IsClosureName(name->toCppString());
177 Attr attrs = this->attrs;
178 if (preClass && preClass->attrs() & AttrInterface) {
179 attrs |= AttrAbstract;
181 if (attrs & AttrPersistent &&
182 ((RuntimeOption::EvalJitEnableRenameFunction && !isGenerated) ||
183 (!RuntimeOption::RepoAuthoritative && SystemLib::s_inited) ||
184 attrs & AttrInterceptable)) {
185 if (attrs & AttrBuiltin) {
186 SystemLib::s_anyNonPersistentBuiltins = true;
188 attrs = Attr(attrs & ~AttrPersistent);
190 if (!RuntimeOption::RepoAuthoritative) {
191 // In non-RepoAuthoritative mode, any function could get a VarEnv because
192 // of evalPHPDebugger.
193 attrs |= AttrMayUseVV;
194 } else if (RuntimeOption::EvalJitEnableRenameFunction &&
195 !name->empty() &&
196 !Func::isSpecial(name) &&
197 !isClosureBody) {
198 // intercepted functions need to pass all args through
199 // to the interceptee
200 attrs |= AttrMayUseVV;
202 if (isVariadic()) { attrs |= AttrVariadicParam; }
204 if (!containsCalls) { attrs |= AttrPhpLeafFn; }
206 assert(!m_pce == !preClass);
207 auto f = m_ue.newFunc(this, unit, name, attrs, params.size());
209 f->m_isPreFunc = !!preClass;
211 bool const needsExtendedSharedData =
212 m_info ||
213 m_builtinFuncPtr ||
214 m_nativeFuncPtr ||
215 (attrs & AttrNative) ||
216 line2 - line1 >= Func::kSmallDeltaLimit ||
217 past - base >= Func::kSmallDeltaLimit;
219 f->m_shared.reset(
220 needsExtendedSharedData
221 ? new Func::ExtendedSharedData(preClass, base, past, line1, line2,
222 top, docComment)
223 : new Func::SharedData(preClass, base, past,
224 line1, line2, top, docComment)
227 f->init(params.size());
229 if (auto const ex = f->extShared()) {
230 ex->m_hasExtendedSharedData = true;
231 ex->m_builtinFuncPtr = m_builtinFuncPtr;
232 ex->m_nativeFuncPtr = m_nativeFuncPtr;
233 ex->m_info = m_info;
234 ex->m_line2 = line2;
235 ex->m_past = past;
236 ex->m_returnByValue = false;
239 std::vector<Func::ParamInfo> fParams;
240 bool usesDoubles = false;
241 for (unsigned i = 0; i < params.size(); ++i) {
242 Func::ParamInfo pi = params[i];
243 if (pi.builtinType == KindOfDouble) usesDoubles = true;
244 if (pi.isVariadic()) {
245 pi.builtinType = KindOfArray;
247 f->appendParam(params[i].byRef, pi, fParams);
250 f->shared()->m_returnType = returnType;
251 f->shared()->m_localNames.create(m_localNames);
252 f->shared()->m_numLocals = m_numLocals;
253 f->shared()->m_numIterators = m_numIterators;
254 f->m_maxStackCells = maxStackCells;
255 f->shared()->m_staticVars = staticVars;
256 f->shared()->m_ehtab = toFixed(ehtab);
257 f->shared()->m_fpitab = fpitab;
258 f->shared()->m_isClosureBody = isClosureBody;
259 f->shared()->m_isAsync = isAsync;
260 f->shared()->m_isGenerator = isGenerator;
261 f->shared()->m_isPairGenerator = isPairGenerator;
262 f->shared()->m_userAttributes = userAttributes;
263 f->shared()->m_retTypeConstraint = retTypeConstraint;
264 f->shared()->m_retUserType = retUserType;
265 f->shared()->m_originalFilename = originalFilename;
266 f->shared()->m_isGenerated = isGenerated;
268 if (attrs & AttrNative) {
269 auto const ex = f->extShared();
271 auto const& info = Native::GetBuiltinFunction(
272 name,
273 m_pce ? m_pce->name() : nullptr,
274 f->isStatic()
277 auto const nif = info.ptr;
278 if (nif) {
279 Attr dummy = AttrNone;
280 int nativeAttrs = parseNativeAttributes(dummy);
281 if (nativeAttrs & Native::AttrZendCompat) {
282 ex->m_nativeFuncPtr = nif;
283 ex->m_builtinFuncPtr = zend_wrap_func;
284 } else {
285 if (parseNativeAttributes(dummy) & Native::AttrActRec) {
286 ex->m_builtinFuncPtr = nif;
287 ex->m_nativeFuncPtr = nullptr;
288 } else {
289 ex->m_nativeFuncPtr = nif;
290 ex->m_builtinFuncPtr = Native::getWrapper(m_pce, usesDoubles);
292 if (info.sig.ret == Native::NativeSig::Type::MixedTV) {
293 ex->m_returnByValue = true;
295 int extra =
296 (attrs & AttrNumArgs ? 1 : 0) +
297 (isMethod() ? 1 : 0);
298 assert(info.sig.args.size() == params.size() + extra);
299 for (auto i = params.size(); i--; ) {
300 switch (info.sig.args[extra + i]) {
301 case Native::NativeSig::Type::ObjectArg:
302 case Native::NativeSig::Type::StringArg:
303 case Native::NativeSig::Type::ArrayArg:
304 case Native::NativeSig::Type::ResourceArg:
305 case Native::NativeSig::Type::OutputArg:
306 case Native::NativeSig::Type::MixedTV:
307 fParams[i].nativeArg = true;
308 break;
309 default:
310 break;
315 } else {
316 ex->m_builtinFuncPtr = Native::unimplementedWrapper;
320 f->finishedEmittingParams(fParams);
321 return f;
324 template<class SerDe>
325 void FuncEmitter::serdeMetaData(SerDe& sd) {
326 // NOTE: name, top, and a few other fields currently serialized
327 // outside of this.
328 sd(line1)
329 (line2)
330 (base)
331 (past)
332 (attrs)
333 (returnType)
334 (docComment)
335 (m_numLocals)
336 (m_numIterators)
337 (maxStackCells)
338 (isClosureBody)
339 (isAsync)
340 (isGenerator)
341 (isPairGenerator)
342 (containsCalls)
344 (params)
345 (m_localNames)
346 (staticVars)
347 (ehtab)
348 (fpitab)
349 (userAttributes)
350 (retTypeConstraint)
351 (retUserType)
352 (originalFilename)
357 ///////////////////////////////////////////////////////////////////////////////
358 // Locals, iterators, and parameters.
360 void FuncEmitter::allocVarId(const StringData* name) {
361 assert(name != nullptr);
362 // Unnamed locals are segregated (they all come after the named locals).
363 assert(m_numUnnamedLocals == 0);
364 UNUSED Id id;
365 if (m_localNames.find(name) == m_localNames.end()) {
366 id = (m_numLocals++);
367 assert(id == (int)m_localNames.size());
368 m_localNames.add(name, name);
372 Id FuncEmitter::allocIterator() {
373 assert(m_numIterators >= m_nextFreeIterator);
374 Id id = m_nextFreeIterator++;
375 if (m_numIterators < m_nextFreeIterator) {
376 m_numIterators = m_nextFreeIterator;
378 return id;
381 Id FuncEmitter::allocUnnamedLocal() {
382 ++m_activeUnnamedLocals;
383 if (m_activeUnnamedLocals > m_numUnnamedLocals) {
384 ++m_numLocals;
385 ++m_numUnnamedLocals;
387 return m_numLocals - m_numUnnamedLocals + (m_activeUnnamedLocals - 1);
391 ///////////////////////////////////////////////////////////////////////////////
392 // Unit tables.
394 EHEntEmitter& FuncEmitter::addEHEnt() {
395 assert(!m_ehTabSorted
396 || "should only mark the ehtab as sorted after adding all of them");
397 ehtab.push_back(EHEntEmitter());
398 ehtab.back().m_parentIndex = 7777;
399 return ehtab.back();
402 FPIEnt& FuncEmitter::addFPIEnt() {
403 fpitab.push_back(FPIEnt());
404 return fpitab.back();
407 namespace {
410 * Ordering on EHEnts where e1 < e2 iff
412 * a) e1 and e2 do not overlap, and e1 comes first
413 * b) e1 encloses e2
414 * c) e1 and e2 have the same region, but e1 is a Catch funclet and
415 * e2 is a Fault funclet.
417 struct EHEntComp {
418 bool operator()(const EHEntEmitter& e1, const EHEntEmitter& e2) const {
419 if (e1.m_base == e2.m_base) {
420 if (e1.m_past == e2.m_past) {
421 static_assert(!static_cast<uint8_t>(EHEnt::Type::Catch),
422 "Catch should be the smallest type");
423 return e1.m_type < e2.m_type;
425 return e1.m_past > e2.m_past;
427 return e1.m_base < e2.m_base;
433 void FuncEmitter::sortEHTab() {
434 if (m_ehTabSorted) return;
436 std::sort(ehtab.begin(), ehtab.end(), EHEntComp());
438 for (unsigned int i = 0; i < ehtab.size(); i++) {
439 ehtab[i].m_parentIndex = -1;
440 for (int j = i - 1; j >= 0; j--) {
441 if (ehtab[j].m_past >= ehtab[i].m_past) {
442 // parent EHEnt better enclose this one.
443 assert(ehtab[j].m_base <= ehtab[i].m_base);
444 ehtab[i].m_parentIndex = j;
445 break;
450 setEHTabIsSorted();
453 void FuncEmitter::sortFPITab(bool load) {
454 // Sort it and fill in parent info
455 std::sort(
456 begin(fpitab), end(fpitab),
457 [&] (const FPIEnt& a, const FPIEnt& b) {
458 return a.m_fpushOff < b.m_fpushOff;
461 for (unsigned int i = 0; i < fpitab.size(); i++) {
462 fpitab[i].m_parentIndex = -1;
463 fpitab[i].m_fpiDepth = 1;
464 for (int j = i - 1; j >= 0; j--) {
465 if (fpitab[j].m_fcallOff > fpitab[i].m_fcallOff) {
466 fpitab[i].m_parentIndex = j;
467 fpitab[i].m_fpiDepth = fpitab[j].m_fpiDepth + 1;
468 break;
471 if (!load) {
472 // m_fpOff does not include the space taken up by locals, iterators and
473 // the AR itself. Fix it here.
474 fpitab[i].m_fpOff += m_numLocals
475 + m_numIterators * kNumIterCells
476 + (fpitab[i].m_fpiDepth) * kNumActRecCells;
481 void FuncEmitter::setEHTabIsSorted() {
482 m_ehTabSorted = true;
483 if (!debug) return;
485 Offset curBase = 0;
486 for (size_t i = 0; i < ehtab.size(); ++i) {
487 auto& eh = ehtab[i];
489 // Base offsets must be monotonically increasing.
490 always_assert(curBase <= eh.m_base);
491 curBase = eh.m_base;
493 // Parent should come before, and must enclose this guy.
494 always_assert(eh.m_parentIndex == -1 || eh.m_parentIndex < i);
495 if (eh.m_parentIndex != -1) {
496 auto& parent = ehtab[eh.m_parentIndex];
497 always_assert(parent.m_base <= eh.m_base &&
498 parent.m_past >= eh.m_past);
504 ///////////////////////////////////////////////////////////////////////////////
505 // Complex setters.
507 /* <<__Native>> user attribute causes systemlib declarations
508 * to hook internal (C++) implementation of funcs/methods
510 * The Native attribute may have the following sub-options
511 * "ActRec": The internal function takes a fixed prototype
512 * TypedValue* funcname(ActRec *ar);
513 * Note that systemlib declaration must still be hack annotated
514 * "NoFCallBuiltin": Prevent FCallBuiltin optimization
515 * Effectively forces functions to generate an ActRec
516 * "NoInjection": Do not include this frame in backtraces
517 * "ZendCompat": Use zend compat wrapper
519 * e.g. <<__Native("ActRec")>> function foo():mixed;
521 static const StaticString
522 s_native("__Native"),
523 s_actrec("ActRec"),
524 s_nofcallbuiltin("NoFCallBuiltin"),
525 s_variadicbyref("VariadicByRef"),
526 s_noinjection("NoInjection"),
527 s_zendcompat("ZendCompat"),
528 s_numargs("NumArgs"),
529 s_opcodeimpl("OpCodeImpl");
531 int FuncEmitter::parseNativeAttributes(Attr& attrs_) const {
532 int ret = Native::AttrNone;
534 auto it = userAttributes.find(s_native.get());
535 assert(it != userAttributes.end());
536 const TypedValue userAttr = it->second;
537 assert(userAttr.m_type == KindOfArray);
538 for (ArrayIter it(userAttr.m_data.parr); it; ++it) {
539 Variant userAttrVal = it.second();
540 if (userAttrVal.isString()) {
541 String userAttrStrVal = userAttrVal.toString();
542 if (userAttrStrVal.get()->isame(s_actrec.get())) {
543 ret |= Native::AttrActRec;
544 attrs_ |= AttrMayUseVV;
545 } else if (userAttrStrVal.get()->isame(s_nofcallbuiltin.get())) {
546 attrs_ |= AttrNoFCallBuiltin;
547 } else if (userAttrStrVal.get()->isame(s_variadicbyref.get())) {
548 attrs_ |= AttrVariadicByRef;
549 } else if (userAttrStrVal.get()->isame(s_noinjection.get())) {
550 attrs_ |= AttrNoInjection;
551 } else if (userAttrStrVal.get()->isame(s_zendcompat.get())) {
552 ret |= Native::AttrZendCompat;
553 // ZendCompat implies ActRec, no FCallBuiltin
554 attrs_ |= AttrMayUseVV | AttrNoFCallBuiltin;
555 ret |= Native::AttrActRec;
556 } else if (userAttrStrVal.get()->isame(s_numargs.get())) {
557 attrs_ |= AttrNumArgs;
558 } else if (userAttrStrVal.get()->isame(s_opcodeimpl.get())) {
559 ret |= Native::AttrOpCodeImpl;
563 return ret;
566 void FuncEmitter::setBuiltinFunc(const ClassInfo::MethodInfo* info,
567 BuiltinFunction bif, BuiltinFunction nif,
568 Offset base_) {
569 assert(info);
570 m_info = info;
571 Attr attrs_ = AttrBuiltin;
572 if (info->attribute & ClassInfo::RefVariableArguments) {
573 attrs_ |= AttrVariadicByRef;
575 if (info->attribute & ClassInfo::IsReference) {
576 attrs_ |= AttrReference;
578 if (info->attribute & ClassInfo::NoInjection) {
579 attrs_ |= AttrNoInjection;
581 if (info->attribute & ClassInfo::NoFCallBuiltin) {
582 attrs_ |= AttrNoFCallBuiltin;
584 if (info->attribute & ClassInfo::ParamCoerceModeNull) {
585 attrs_ |= AttrParamCoerceModeNull;
586 } else if (info->attribute & ClassInfo::ParamCoerceModeFalse) {
587 attrs_ |= AttrParamCoerceModeFalse;
589 if (pce()) {
590 if (info->attribute & ClassInfo::IsStatic) {
591 attrs_ |= AttrStatic;
593 if (info->attribute & ClassInfo::IsFinal) {
594 attrs_ |= AttrFinal;
596 if (info->attribute & ClassInfo::IsAbstract) {
597 attrs_ |= AttrAbstract;
599 if (info->attribute & ClassInfo::IsPrivate) {
600 attrs_ |= AttrPrivate;
601 } else if (info->attribute & ClassInfo::IsProtected) {
602 attrs_ |= AttrProtected;
603 } else {
604 attrs_ |= AttrPublic;
606 } else if (info->attribute & ClassInfo::AllowOverride) {
607 attrs_ |= AttrAllowOverride;
610 returnType = info->returnType;
611 docComment = makeStaticString(info->docComment);
612 setLocation(0, 0);
613 setBuiltinFunc(bif, nif, attrs_, base_);
615 for (unsigned i = 0; i < info->parameters.size(); ++i) {
616 // For builtin only, we use a dummy ParamInfo
617 FuncEmitter::ParamInfo pi;
618 const auto& parameter = info->parameters[i];
619 pi.byRef = parameter->attribute & ClassInfo::IsReference;
620 pi.builtinType = parameter->argType;
621 appendParam(makeStaticString(parameter->name), pi);
625 void FuncEmitter::setBuiltinFunc(BuiltinFunction bif, BuiltinFunction nif,
626 Attr attrs_, Offset base_) {
627 assert(bif);
628 m_builtinFuncPtr = bif;
629 m_nativeFuncPtr = nif;
630 base = base_;
631 top = true;
632 // TODO: Task #1137917: See if we can avoid marking most builtins with
633 // "MayUseVV" and still make things work
634 attrs = attrs_ | AttrBuiltin | AttrSkipFrame | AttrMayUseVV;
638 ///////////////////////////////////////////////////////////////////////////////
639 // FuncRepoProxy.
641 FuncRepoProxy::FuncRepoProxy(Repo& repo)
642 : RepoProxy(repo),
643 insertFunc{InsertFuncStmt(repo, 0), InsertFuncStmt(repo, 1)},
644 getFuncs{GetFuncsStmt(repo, 0), GetFuncsStmt(repo, 1)}
647 FuncRepoProxy::~FuncRepoProxy() {
650 void FuncRepoProxy::createSchema(int repoId, RepoTxn& txn) {
651 std::stringstream ssCreate;
652 ssCreate << "CREATE TABLE " << m_repo.table(repoId, "Func")
653 << "(unitSn INTEGER, funcSn INTEGER, preClassId INTEGER,"
654 " name TEXT, top INTEGER,"
655 " extraData BLOB,"
656 " PRIMARY KEY (unitSn, funcSn));";
657 txn.exec(ssCreate.str());
660 void FuncRepoProxy::InsertFuncStmt
661 ::insert(const FuncEmitter& fe,
662 RepoTxn& txn, int64_t unitSn, int funcSn,
663 Id preClassId, const StringData* name,
664 bool top) {
665 if (!prepared()) {
666 std::stringstream ssInsert;
667 ssInsert << "INSERT INTO " << m_repo.table(m_repoId, "Func")
668 << " VALUES(@unitSn, @funcSn, @preClassId, @name, "
669 " @top, @extraData);";
670 txn.prepare(*this, ssInsert.str());
673 BlobEncoder extraBlob;
674 RepoTxnQuery query(txn, *this);
675 query.bindInt64("@unitSn", unitSn);
676 query.bindInt("@funcSn", funcSn);
677 query.bindId("@preClassId", preClassId);
678 query.bindStaticString("@name", name);
679 query.bindBool("@top", top);
680 const_cast<FuncEmitter&>(fe).serdeMetaData(extraBlob);
681 query.bindBlob("@extraData", extraBlob, /* static */ true);
682 query.exec();
685 void FuncRepoProxy::GetFuncsStmt
686 ::get(UnitEmitter& ue) {
687 RepoTxn txn(m_repo);
688 if (!prepared()) {
689 std::stringstream ssSelect;
690 ssSelect << "SELECT funcSn,preClassId,name,top,extraData "
691 "FROM "
692 << m_repo.table(m_repoId, "Func")
693 << " WHERE unitSn == @unitSn ORDER BY funcSn ASC;";
694 txn.prepare(*this, ssSelect.str());
696 RepoTxnQuery query(txn, *this);
697 query.bindInt64("@unitSn", ue.m_sn);
698 do {
699 query.step();
700 if (query.row()) {
701 int funcSn; /**/ query.getInt(0, funcSn);
702 Id preClassId; /**/ query.getId(1, preClassId);
703 StringData* name; /**/ query.getStaticString(2, name);
704 bool top; /**/ query.getBool(3, top);
705 BlobDecoder extraBlob = /**/ query.getBlob(4);
707 FuncEmitter* fe;
708 if (preClassId < 0) {
709 fe = ue.newFuncEmitter(name);
710 } else {
711 PreClassEmitter* pce = ue.pce(preClassId);
712 fe = ue.newMethodEmitter(name, pce);
713 bool added UNUSED = pce->addMethod(fe);
714 assert(added);
716 assert(fe->sn() == funcSn);
717 fe->top = top;
718 fe->serdeMetaData(extraBlob);
719 if (!SystemLib::s_inited && !fe->isPseudoMain()) {
720 assert(fe->attrs & AttrBuiltin);
721 if (preClassId < 0) {
722 assert(fe->attrs & AttrPersistent);
723 assert(fe->attrs & AttrUnique);
724 assert(fe->attrs & AttrSkipFrame);
727 fe->setEHTabIsSorted();
728 fe->finish(fe->past, true);
729 ue.recordFunction(fe);
731 } while (!query.done());
732 txn.commit();
735 ///////////////////////////////////////////////////////////////////////////////