Better reffiness checks
[hiphop-php.git] / hphp / runtime / vm / func.cpp
blob8f3cd07d245fb30d9e491ba3be2e168c2e77625d
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/func.h"
18 #include <iostream>
19 #include <boost/scoped_ptr.hpp>
21 #include "hphp/runtime/base/base_includes.h"
22 #include "hphp/util/util.h"
23 #include "hphp/util/trace.h"
24 #include "hphp/util/debug.h"
25 #include "hphp/runtime/base/strings.h"
26 #include "hphp/runtime/vm/core_types.h"
27 #include "hphp/runtime/vm/runtime.h"
28 #include "hphp/runtime/vm/repo.h"
29 #include "hphp/runtime/vm/jit/targetcache.h"
30 #include "hphp/runtime/base/file_repository.h"
31 #include "hphp/runtime/vm/jit/translator-x64.h"
32 #include "hphp/runtime/vm/blob_helper.h"
33 #include "hphp/runtime/vm/func_inline.h"
34 #include "hphp/system/lib/systemlib.h"
35 #include "hphp/runtime/vm/bytecode.h"
37 namespace HPHP {
39 TRACE_SET_MOD(hhbc);
40 const StringData* Func::s___call = StringData::GetStaticString("__call");
41 const StringData* Func::s___callStatic =
42 StringData::GetStaticString("__callStatic");
44 //=============================================================================
45 // Func.
47 static void decl_incompat(const PreClass* implementor,
48 const Func* imeth) {
49 const char* name = imeth->name()->data();
50 raise_error("Declaration of %s::%s() must be compatible with "
51 "that of %s::%s()", implementor->name()->data(), name,
52 imeth->cls()->preClass()->name()->data(), name);
55 // Check compatibility vs interface and abstract declarations
56 void Func::parametersCompat(const PreClass* preClass, const Func* imeth) const {
57 const Func::ParamInfoVec& params = this->params();
58 const Func::ParamInfoVec& iparams = imeth->params();
59 // Verify that meth has at least as many parameters as imeth.
60 if ((params.size() < iparams.size())) {
61 decl_incompat(preClass, imeth);
63 // Verify that the typehints for meth's parameters are compatible with
64 // imeth's corresponding parameter typehints.
65 unsigned firstOptional = 0;
66 for (unsigned i = 0; i < iparams.size(); ++i) {
67 if (!params[i].typeConstraint().compat(iparams[i].typeConstraint())) {
68 decl_incompat(preClass, imeth);
70 if (!iparams[i].hasDefaultValue()) {
71 // The leftmost of imeth's contiguous trailing optional parameters
72 // must start somewhere to the right of this parameter.
73 firstOptional = i + 1;
76 // Verify that meth provides defaults, starting with the parameter that
77 // corresponds to the leftmost of imeth's contiguous trailing optional
78 // parameters.
79 for (unsigned i = firstOptional; i < params.size(); ++i) {
80 if (!params[i].hasDefaultValue()) {
81 decl_incompat(preClass, imeth);
86 static FuncId s_nextFuncId = 0;
88 void Func::setFuncId(FuncId id) {
89 assert(m_funcId == InvalidFuncId);
90 assert(id != InvalidFuncId);
91 m_funcId = id;
94 void Func::setNewFuncId() {
95 assert(m_funcId == InvalidFuncId);
96 m_funcId = static_cast<FuncId>(__sync_fetch_and_add(&s_nextFuncId, 1));
99 void Func::setFullName() {
100 assert(m_name->isStatic());
101 if (m_cls) {
102 m_fullName = StringData::GetStaticString(
103 std::string(m_cls->name()->data()) + "::" + m_name->data());
104 } else {
105 m_fullName = m_name;
106 m_namedEntity = Unit::GetNamedEntity(m_name);
108 if (RuntimeOption::DynamicInvokeFunctions.size()) {
109 if (RuntimeOption::DynamicInvokeFunctions.find(m_fullName->data()) !=
110 RuntimeOption::DynamicInvokeFunctions.end()) {
111 m_attrs = Attr(m_attrs | AttrDynamicInvoke);
116 void Func::initPrologues(int numParams, bool isGenerator) {
117 m_funcBody = (TCA)HPHP::Transl::funcBodyHelperThunk;
119 int maxNumPrologues = Func::getMaxNumPrologues(numParams);
120 int numPrologues =
121 maxNumPrologues > kNumFixedPrologues ? maxNumPrologues
122 : kNumFixedPrologues;
124 TRACE(2, "initPrologues func %p %d\n", this, numPrologues);
125 for (int i = 0; i < numPrologues; i++) {
126 m_prologueTable[i] = (TCA)HPHP::Transl::fcallHelperThunk;
130 void Func::init(int numParams, bool isGenerator) {
131 // For methods, we defer setting the full name until m_cls is initialized
132 m_maybeIntercepted = -1;
133 if (!preClass()) {
134 setNewFuncId();
135 setFullName();
136 } else {
137 m_fullName = 0;
139 if (isSpecial(m_name)) {
141 * i) We dont want these compiler generated functions to
142 * appear in backtraces.
144 * ii) 86sinit and 86pinit construct NameValueTableWrappers
145 * on the stack. So we MUST NOT allow those to leak into
146 * the backtrace (since the backtrace will outlive the
147 * variables).
149 m_attrs = m_attrs | AttrNoInjection;
151 #ifdef DEBUG
152 m_magic = kMagic;
153 #endif
154 assert(m_name);
155 initPrologues(numParams, isGenerator);
158 void* Func::allocFuncMem(
159 const StringData* name, int numParams, bool needsNextClonedClosure) {
160 int maxNumPrologues = Func::getMaxNumPrologues(numParams);
161 int numExtraPrologues =
162 maxNumPrologues > kNumFixedPrologues ?
163 maxNumPrologues - kNumFixedPrologues :
165 size_t funcSize = sizeof(Func) + numExtraPrologues * sizeof(unsigned char*);
166 if (needsNextClonedClosure) {
167 funcSize += sizeof(Func*);
169 void* mem = Util::low_malloc(funcSize);
170 if (needsNextClonedClosure) {
171 // make room for nextClonedClosure to work
172 Func** startOfFunc = (Func**) mem;
173 *startOfFunc = nullptr;
174 return startOfFunc + 1;
176 return mem;
179 Func::Func(Unit& unit, Id id, int line1, int line2,
180 Offset base, Offset past, const StringData* name,
181 Attr attrs, bool top, const StringData* docComment, int numParams,
182 bool isGenerator)
183 : m_unit(&unit)
184 , m_cls(nullptr)
185 , m_baseCls(nullptr)
186 , m_name(name)
187 , m_namedEntity(nullptr)
188 , m_refBitVal(0)
189 , m_cachedOffset(0)
190 , m_maxStackCells(0)
191 , m_numParams(0)
192 , m_attrs(attrs)
193 , m_funcId(InvalidFuncId)
194 , m_hasPrivateAncestor(false)
196 m_shared = new SharedData(nullptr, id, base, past, line1, line2,
197 top, docComment);
198 init(numParams, isGenerator);
201 // Class method
202 Func::Func(Unit& unit, PreClass* preClass, int line1, int line2, Offset base,
203 Offset past, const StringData* name, Attr attrs,
204 bool top, const StringData* docComment, int numParams,
205 bool isGenerator)
206 : m_unit(&unit)
207 , m_cls(nullptr)
208 , m_baseCls(nullptr)
209 , m_name(name)
210 , m_namedEntity(nullptr)
211 , m_refBitVal(0)
212 , m_cachedOffset(0)
213 , m_maxStackCells(0)
214 , m_numParams(0)
215 , m_attrs(attrs)
216 , m_funcId(InvalidFuncId)
217 , m_hasPrivateAncestor(false)
219 Id id = -1;
220 m_shared = new SharedData(preClass, id, base, past, line1, line2,
221 top, docComment);
222 init(numParams, isGenerator);
225 Func::~Func() {
226 if (m_fullName != nullptr && m_maybeIntercepted != -1) {
227 unregister_intercept_flag(fullNameRef(), &m_maybeIntercepted);
229 #ifdef DEBUG
230 validate();
231 m_magic = ~m_magic;
232 #endif
235 void Func::destroy(Func* func) {
236 void* mem = func;
237 if (func->isClosureBody() || func->isGeneratorFromClosure()) {
238 Func** startOfFunc = (Func**) mem;
239 mem = startOfFunc - 1; // move back by a pointer
241 func->~Func();
242 Util::low_free(mem);
245 Func* Func::clone() const {
246 Func* f = new (allocFuncMem(
247 m_name,
248 m_numParams,
249 isClosureBody() || isGeneratorFromClosure()
250 )) Func(*this);
251 f->initPrologues(m_numParams, isGenerator());
252 f->m_funcId = InvalidFuncId;
253 return f;
256 const Func* Func::cloneAndSetClass(Class* cls) const {
257 if (const Func* ret = findCachedClone(cls)) {
258 return ret;
261 static Mutex s_clonedFuncListMutex;
262 Lock l(s_clonedFuncListMutex);
263 // Check again now that I'm the writer
264 if (const Func* ret = findCachedClone(cls)) {
265 return ret;
268 Func* clonedFunc = clone();
269 clonedFunc->setNewFuncId();
270 clonedFunc->setCls(cls);
272 // Save it so we don't have to keep cloning it and retranslating
273 Func** nextFunc = &this->nextClonedClosure();
274 while (*nextFunc) {
275 nextFunc = &nextFunc[0]->nextClonedClosure();
277 *nextFunc = clonedFunc;
279 return clonedFunc;
282 const Func* Func::findCachedClone(Class* cls) const {
283 const Func* nextFunc = this;
284 while (nextFunc) {
285 if (nextFunc->cls() == cls) {
286 return nextFunc;
288 nextFunc = nextFunc->nextClonedClosure();
290 return nullptr;
293 void Func::rename(const StringData* name) {
294 m_name = name;
295 setFullName();
296 // load the renamed function
297 Unit::loadFunc(this);
300 int Func::numSlotsInFrame() const {
301 return shared()->m_numLocals + shared()->m_numIterators * kNumIterCells;
304 bool Func::checkIterScope(Offset o, Id iterId, bool& itRef) const {
305 const EHEntVec& ehtab = shared()->m_ehtab;
306 assert(o >= base() && o < past());
307 for (unsigned i = 0, n = ehtab.size(); i < n; i++) {
308 const EHEnt* eh = &ehtab[i];
309 if (eh->m_ehtype == EHEnt::EHType_Fault &&
310 eh->m_base <= o && o < eh->m_past &&
311 eh->m_iterId == iterId) {
312 itRef = eh->m_itRef;
313 return true;
316 return false;
319 const EHEnt* Func::findEH(Offset o) const {
320 assert(o >= base() && o < past());
321 const EHEnt* eh = nullptr;
322 unsigned int i;
324 const EHEntVec& ehtab = shared()->m_ehtab;
325 for (i = 0; i < ehtab.size(); i++) {
326 if (ehtab[i].m_base <= o && o < ehtab[i].m_past) {
327 eh = &ehtab[i];
330 return eh;
333 const FPIEnt* Func::findFPI(Offset o) const {
334 assert(o >= base() && o < past());
335 const FPIEnt* fe = nullptr;
336 unsigned int i;
338 const FPIEntVec& fpitab = shared()->m_fpitab;
339 for (i = 0; i < fpitab.size(); i++) {
341 * We consider the "FCall" instruction part of the FPI region, but
342 * the corresponding push is not considered part of it. (This
343 * means all offsets in the FPI region will have the partial
344 * ActRec on the stack.)
346 if (fpitab[i].m_fpushOff < o && o <= fpitab[i].m_fcallOff) {
347 fe = &fpitab[i];
350 return fe;
353 const FPIEnt* Func::findPrecedingFPI(Offset o) const {
354 assert(o >= base() && o < past());
355 const FPIEntVec& fpitab = shared()->m_fpitab;
356 assert(fpitab.size());
357 const FPIEnt* fe = &fpitab[0];
358 unsigned int i;
359 for (i = 1; i < fpitab.size(); i++) {
360 const FPIEnt* cur = &fpitab[i];
361 if (o > cur->m_fcallOff &&
362 fe->m_fcallOff < cur->m_fcallOff) {
363 fe = cur;
366 assert(fe);
367 return fe;
370 bool Func::isClonedClosure() const {
371 if (!isClosureBody()) return false;
372 if (!cls()) return true;
373 return cls()->lookupMethod(name()) != this;
376 bool Func::isNameBindingImmutable(const Unit* fromUnit) const {
377 if (RuntimeOption::EvalJitEnableRenameFunction ||
378 m_attrs & AttrDynamicInvoke) {
379 return false;
382 if (isBuiltin()) {
383 return true;
386 if (isUnique() && RuntimeOption::RepoAuthoritative) {
387 return true;
390 // Defined at top level, in the same unit as the caller. This precludes
391 // conditionally defined functions and cross-module calls -- both phenomena
392 // can change name->Func mappings during the lifetime of a TC.
393 return top() && (fromUnit == m_unit);
396 bool Func::byRef(int32_t arg) const {
397 const uint64_t* ref = &m_refBitVal;
398 assert(arg >= 0);
399 if (UNLIKELY(arg >= kBitsPerQword)) {
400 // Super special case. A handful of builtins are varargs functions where the
401 // (not formally declared) varargs are pass-by-reference. psychedelic-kitten
402 if (arg >= m_numParams) {
403 return m_attrs & AttrVariadicByRef;
405 ref = &shared()->m_refBitPtr[(uint32_t)arg / kBitsPerQword - 1];
407 int bit = (uint32_t)arg % kBitsPerQword;
408 return *ref & (1ull << bit);
411 bool Func::mustBeRef(int32_t arg) const {
412 if (byRef(arg)) {
413 return arg < m_numParams || !(m_attrs & AttrVariadicByRef) ||
414 !info() || !(info()->attribute & ClassInfo::MixedVariableArguments);
416 return false;
419 void Func::appendParam(bool ref, const Func::ParamInfo& info,
420 std::vector<ParamInfo>& pBuilder) {
421 int qword = m_numParams / kBitsPerQword;
422 int bit = m_numParams % kBitsPerQword;
423 m_numParams++;
424 uint64_t* refBits = &m_refBitVal;
425 // Grow args, if necessary.
426 if (qword) {
427 if (bit == 0) {
428 shared()->m_refBitPtr = (uint64_t*)
429 realloc(shared()->m_refBitPtr, qword * sizeof(uint64_t));
431 refBits = shared()->m_refBitPtr + qword - 1;
434 if (bit == 0) {
435 // The new word is either zerod or set to 1, depending on whether
436 // we are one of the special builtins that takes variadic
437 // reference arguments. This is for use in the translator.
438 *refBits = (m_attrs & AttrVariadicByRef) ? -1ull : 0;
441 assert(!(*refBits & (uint64_t(1) << bit)) == !(m_attrs & AttrVariadicByRef));
442 *refBits &= ~(1ull << bit);
443 *refBits |= uint64_t(ref) << bit;
444 pBuilder.push_back(info);
447 Id Func::lookupVarId(const StringData* name) const {
448 assert(name != nullptr);
449 return shared()->m_localNames.findIndex(name);
452 void Func::prettyPrint(std::ostream& out) const {
453 if (isPseudoMain()) {
454 out << "Pseudo-main";
455 } else if (preClass() != nullptr) {
456 out << "Method ";
457 if (m_attrs & AttrStatic) { out << "static "; }
458 if (m_attrs & AttrPublic) { out << "public "; }
459 if (m_attrs & AttrProtected) { out << "protected "; }
460 if (m_attrs & AttrPrivate) { out << "private "; }
461 if (m_attrs & AttrAbstract) { out << "abstract "; }
462 if (m_attrs & AttrFinal) { out << "final "; }
463 if (m_attrs & AttrPhpLeafFn) { out << "(leaf) "; }
464 if (cls() != nullptr) {
465 out << fullName()->data();
466 } else {
467 out << preClass()->name()->data() << "::" << m_name->data();
469 } else {
470 out << "Function " << m_name->data();
473 if (m_attrs & AttrHot) out << " (hot)";
475 out << " at " << base();
476 if (shared()->m_id != -1) {
477 out << " (ID " << shared()->m_id << ")";
479 out << std::endl;
480 const ParamInfoVec& params = shared()->m_params;
481 for (uint i = 0; i < params.size(); ++i) {
482 if (params[i].funcletOff() != InvalidAbsoluteOffset) {
483 out << " DV for parameter " << i << " at " << params[i].funcletOff();
484 if (params[i].phpCode()) {
485 out << " = " << params[i].phpCode()->data();
487 out << std::endl;
490 const EHEntVec& ehtab = shared()->m_ehtab;
491 for (EHEntVec::const_iterator it = ehtab.begin(); it != ehtab.end(); ++it) {
492 bool catcher = it->m_ehtype == EHEnt::EHType_Catch;
493 out << " EH " << (catcher ? "Catch" : "Fault") << " for " <<
494 it->m_base << ":" << it->m_past;
495 if (it->m_parentIndex != -1) {
496 out << " outer EH " << it->m_parentIndex;
498 if (it->m_iterId != -1) {
499 out << " iterId " << it->m_iterId;
500 out << " itRef " << (it->m_itRef ? "true" : "false");
502 if (catcher) {
503 out << std::endl;
504 for (EHEnt::CatchVec::const_iterator it2 = it->m_catches.begin();
505 it2 != it->m_catches.end(); ++it2) {
506 out << " Handle " << m_unit->lookupLitstrId(it2->first)->data()
507 << " at " << it2->second;
509 } else {
510 out << " to " << it->m_fault;
512 if (it->m_parentIndex != -1) {
513 out << " parentIndex " << it->m_parentIndex;
515 out << std::endl;
519 HphpArray* Func::getStaticLocals() const {
520 return g_vmContext->getFuncStaticCtx(this);
523 void Func::getFuncInfo(ClassInfo::MethodInfo* mi) const {
524 assert(mi);
525 if (info() != nullptr) {
526 // Very large operator=() invocation.
527 *mi = *info();
528 // Deep copy the vectors of mi-owned pointers.
529 cloneMembers(mi->parameters);
530 cloneMembers(mi->staticVariables);
531 } else {
532 // hphpc sets the ClassInfo::VariableArguments attribute if the method
533 // contains a call to func_get_arg, func_get_args, or func_num_args. We
534 // don't do this in the VM currently and hopefully we never will need to.
535 int attr = 0;
536 if (m_attrs & AttrReference) attr |= ClassInfo::IsReference;
537 if (m_attrs & AttrAbstract) attr |= ClassInfo::IsAbstract;
538 if (m_attrs & AttrFinal) attr |= ClassInfo::IsFinal;
539 if (m_attrs & AttrProtected) attr |= ClassInfo::IsProtected;
540 if (m_attrs & AttrPrivate) attr |= ClassInfo::IsPrivate;
541 if (m_attrs & AttrStatic) attr |= ClassInfo::IsStatic;
542 if (!(attr & ClassInfo::IsProtected || attr & ClassInfo::IsPrivate)) {
543 attr |= ClassInfo::IsPublic;
545 if (preClass() &&
546 (!strcasecmp(m_name->data(), "__construct") ||
547 (!(preClass()->attrs() & AttrTrait) &&
548 !strcasecmp(m_name->data(), preClass()->name()->data()) &&
549 !preClass()->hasMethod(String("__construct").get())))) {
550 attr |= ClassInfo::IsConstructor;
552 if (attr == 0) attr = ClassInfo::IsNothing;
553 mi->attribute = (ClassInfo::Attribute)attr;
554 mi->name = m_name->data();
555 mi->file = m_unit->filepath()->data();
556 mi->line1 = line1();
557 mi->line2 = line2();
558 if (docComment() && !docComment()->empty()) {
559 mi->docComment = docComment()->data();
561 // Get the parameter info
562 for (unsigned i = 0; i < unsigned(m_numParams); ++i) {
563 ClassInfo::ParameterInfo* pi = new ClassInfo::ParameterInfo;
564 attr = 0;
565 if (byRef(i)) {
566 attr |= ClassInfo::IsReference;
568 if (attr == 0) {
569 attr = ClassInfo::IsNothing;
571 const ParamInfoVec& params = shared()->m_params;
572 const ParamInfo& fpi = params[i];
573 pi->attribute = (ClassInfo::Attribute)attr;
574 pi->name = shared()->m_localNames[i]->data();
575 if (params.size() <= i || !fpi.hasDefaultValue()) {
576 pi->value = nullptr;
577 pi->valueText = "";
578 } else {
579 if (fpi.hasScalarDefaultValue()) {
580 // Most of the time the default value is scalar, so we can
581 // avoid evaling in the common case
582 pi->value = strdup(f_serialize(
583 tvAsVariant((TypedValue*)&fpi.defaultValue())).get()->data());
584 } else {
585 // Eval PHP code to get default value, and serialize the result. Note
586 // that access of undefined class constants can cause the eval() to
587 // fatal. Zend lets such fatals propagate, so don't bother catching
588 // exceptions here.
589 CVarRef v = g_vmContext->getEvaledArg(fpi.phpCode());
590 pi->value = strdup(f_serialize(v).get()->data());
592 // This is a raw char*, but its lifetime should be at least as long
593 // as the the Func*. At this writing, it's a merged anon string
594 // owned by ParamInfo.
595 pi->valueText = fpi.phpCode()->data();
597 pi->type = fpi.typeConstraint().exists() ?
598 fpi.typeConstraint().typeName()->data() : "";
599 for (UserAttributeMap::const_iterator it = fpi.userAttributes().begin();
600 it != fpi.userAttributes().end(); ++it) {
601 // convert the typedvalue to a cvarref and push into pi.
602 auto userAttr = new ClassInfo::UserAttributeInfo;
603 assert(it->first->isStatic());
604 userAttr->name = const_cast<StringData*>(it->first);
605 userAttr->setStaticValue(tvAsCVarRef(&it->second));
606 pi->userAttrs.push_back(userAttr);
608 mi->parameters.push_back(pi);
610 // XXX ConstantInfo is abused to store static variable metadata, and
611 // although ConstantInfo::callbacks provides a mechanism for registering
612 // callbacks, it does not pass enough information through for the callback
613 // functions to know the function context whence the callbacks came.
614 // Furthermore, the callback mechanism isn't employed in a fashion that
615 // would allow repeated introspection to reflect updated values.
616 // Supporting introspection of static variable values will require
617 // different plumbing than currently exists in ConstantInfo.
618 const SVInfoVec& staticVars = shared()->m_staticVars;
619 for (SVInfoVec::const_iterator it = staticVars.begin();
620 it != staticVars.end(); ++it) {
621 ClassInfo::ConstantInfo* ci = new ClassInfo::ConstantInfo;
622 ci->name = *(String*)(&(*it).name);
623 if ((*it).phpCode != nullptr) {
624 ci->valueLen = (*it).phpCode->size();
625 ci->valueText = (*it).phpCode->data();
626 } else {
627 ci->valueLen = 0;
628 ci->valueText = "";
631 mi->staticVariables.push_back(ci);
636 Func::SharedData::SharedData(PreClass* preClass, Id id,
637 Offset base, Offset past, int line1, int line2,
638 bool top, const StringData* docComment)
639 : m_preClass(preClass), m_id(id), m_base(base),
640 m_numLocals(0), m_numIterators(0),
641 m_past(past), m_line1(line1), m_line2(line2),
642 m_info(nullptr), m_refBitPtr(0), m_builtinFuncPtr(nullptr),
643 m_docComment(docComment), m_top(top), m_isClosureBody(false),
644 m_isGenerator(false), m_isGeneratorFromClosure(false),
645 m_hasGeneratorAsBody(false), m_originalFilename(nullptr) {
648 Func::SharedData::~SharedData() {
649 free(m_refBitPtr);
652 void Func::SharedData::atomicRelease() {
653 delete this;
656 Func** Func::getCachedAddr() {
657 assert(!isMethod());
658 return getCachedFuncAddr(m_cachedOffset);
661 void Func::setCached() {
662 setCachedFunc(this, isDebuggerAttached());
665 const Func* Func::getGeneratorBody(const StringData* name) const {
666 if (isNonClosureMethod()) {
667 return cls()->lookupMethod(name);
668 } else {
669 return Unit::lookupFunc(name);
673 //=============================================================================
674 // FuncEmitter.
676 FuncEmitter::FuncEmitter(UnitEmitter& ue, int sn, Id id, const StringData* n)
677 : m_ue(ue)
678 , m_pce(nullptr)
679 , m_sn(sn)
680 , m_id(id)
681 , m_name(n)
682 , m_numLocals(0)
683 , m_numUnnamedLocals(0)
684 , m_activeUnnamedLocals(0)
685 , m_numIterators(0)
686 , m_nextFreeIterator(0)
687 , m_retTypeConstraint(nullptr)
688 , m_returnType(KindOfInvalid)
689 , m_top(false)
690 , m_isClosureBody(false)
691 , m_isGenerator(false)
692 , m_isGeneratorFromClosure(false)
693 , m_hasGeneratorAsBody(false)
694 , m_containsCalls(false)
695 , m_info(nullptr)
696 , m_builtinFuncPtr(nullptr)
697 , m_originalFilename(nullptr)
700 FuncEmitter::FuncEmitter(UnitEmitter& ue, int sn, const StringData* n,
701 PreClassEmitter* pce)
702 : m_ue(ue)
703 , m_pce(pce)
704 , m_sn(sn)
705 , m_name(n)
706 , m_numLocals(0)
707 , m_numUnnamedLocals(0)
708 , m_activeUnnamedLocals(0)
709 , m_numIterators(0)
710 , m_nextFreeIterator(0)
711 , m_retTypeConstraint(nullptr)
712 , m_returnType(KindOfInvalid)
713 , m_top(false)
714 , m_isClosureBody(false)
715 , m_isGenerator(false)
716 , m_isGeneratorFromClosure(false)
717 , m_hasGeneratorAsBody(false)
718 , m_containsCalls(false)
719 , m_info(nullptr)
720 , m_builtinFuncPtr(nullptr)
721 , m_originalFilename(nullptr)
724 FuncEmitter::~FuncEmitter() {
727 void FuncEmitter::init(int line1, int line2, Offset base, Attr attrs, bool top,
728 const StringData* docComment) {
729 m_line1 = line1;
730 m_line2 = line2;
731 m_base = base;
732 m_attrs = attrs;
733 m_top = top;
734 m_docComment = docComment;
735 if (!SystemLib::s_inited) {
736 m_attrs = m_attrs | AttrBuiltin;
737 if (!pce()) m_attrs = m_attrs | AttrSkipFrame;
741 void FuncEmitter::finish(Offset past, bool load) {
742 m_past = past;
743 sortEHTab();
744 sortFPITab(load);
747 EHEnt& FuncEmitter::addEHEnt() {
748 m_ehtab.push_back(EHEnt());
749 return m_ehtab.back();
752 FPIEnt& FuncEmitter::addFPIEnt() {
753 m_fpitab.push_back(FPIEnt());
754 return m_fpitab.back();
757 Id FuncEmitter::newLocal() {
758 return m_numLocals++;
761 void FuncEmitter::appendParam(const StringData* name, const ParamInfo& info) {
762 allocVarId(name);
763 m_params.push_back(info);
766 void FuncEmitter::allocVarId(const StringData* name) {
767 assert(name != nullptr);
768 // Unnamed locals are segregated (they all come after the named locals).
769 assert(m_numUnnamedLocals == 0);
770 UNUSED Id id;
771 if (m_localNames.find(name) == m_localNames.end()) {
772 id = newLocal();
773 assert(id == (int)m_localNames.size());
774 m_localNames.add(name, name);
778 Id FuncEmitter::lookupVarId(const StringData* name) const {
779 assert(this->hasVar(name));
780 return m_localNames.find(name)->second;
783 bool FuncEmitter::hasVar(const StringData* name) const {
784 assert(name != nullptr);
785 return m_localNames.find(name) != m_localNames.end();
788 Id FuncEmitter::allocIterator() {
789 assert(m_numIterators >= m_nextFreeIterator);
790 Id id = m_nextFreeIterator++;
791 if (m_numIterators < m_nextFreeIterator) {
792 m_numIterators = m_nextFreeIterator;
794 return id;
797 void FuncEmitter::freeIterator(Id id) {
798 --m_nextFreeIterator;
799 assert(id == m_nextFreeIterator);
802 void FuncEmitter::setNumIterators(Id numIterators) {
803 assert(m_numIterators == 0);
804 m_numIterators = numIterators;
807 Id FuncEmitter::allocUnnamedLocal() {
808 ++m_activeUnnamedLocals;
809 if (m_activeUnnamedLocals > m_numUnnamedLocals) {
810 newLocal();
811 ++m_numUnnamedLocals;
813 return m_numLocals - m_numUnnamedLocals + (m_activeUnnamedLocals - 1);
816 void FuncEmitter::freeUnnamedLocal(Id id) {
817 assert(m_activeUnnamedLocals > 0);
818 --m_activeUnnamedLocals;
821 void FuncEmitter::setNumLocals(Id numLocals) {
822 assert(numLocals >= m_numLocals);
823 m_numLocals = numLocals;
826 void FuncEmitter::addStaticVar(Func::SVInfo svInfo) {
827 m_staticVars.push_back(svInfo);
830 namespace {
833 * Ordering on EHEnts where e1 < e2 iff
835 * a) e1 and e2 do not overlap, and e1 comes first
836 * b) e1 encloses e2
837 * c) e1 and e2 have the same region, but e1 is a Catch funclet and
838 * e2 is a Fault funclet.
840 struct EHEntComp {
841 bool operator()(const EHEnt& e1, const EHEnt& e2) const {
842 if (e1.m_base == e2.m_base) {
843 if (e1.m_past == e2.m_past) {
844 return e1.m_ehtype == EHEnt::EHType_Catch;
846 return e1.m_past > e2.m_past;
848 return e1.m_base < e2.m_base;
854 void FuncEmitter::sortEHTab() {
855 std::sort(m_ehtab.begin(), m_ehtab.end(), EHEntComp());
857 for (unsigned int i = 0; i < m_ehtab.size(); i++) {
858 m_ehtab[i].m_parentIndex = -1;
859 for (int j = i - 1; j >= 0; j--) {
860 if (m_ehtab[j].m_past >= m_ehtab[i].m_past) {
861 // parent EHEnt better enclose this one.
862 assert(m_ehtab[j].m_base <= m_ehtab[i].m_base);
863 m_ehtab[i].m_parentIndex = j;
864 break;
870 void FuncEmitter::sortFPITab(bool load) {
871 // Sort it and fill in parent info
872 std::sort(m_fpitab.begin(), m_fpitab.end(), FPIEntComp());
873 for (unsigned int i = 0; i < m_fpitab.size(); i++) {
874 m_fpitab[i].m_parentIndex = -1;
875 m_fpitab[i].m_fpiDepth = 1;
876 for (int j = i - 1; j >= 0; j--) {
877 if (m_fpitab[j].m_fcallOff > m_fpitab[i].m_fcallOff) {
878 m_fpitab[i].m_parentIndex = j;
879 m_fpitab[i].m_fpiDepth = m_fpitab[j].m_fpiDepth + 1;
880 break;
883 if (!load) {
884 // m_fpOff does not include the space taken up by locals, iterators and
885 // the AR itself. Fix it here.
886 m_fpitab[i].m_fpOff += m_numLocals
887 + m_numIterators * kNumIterCells
888 + (m_fpitab[i].m_fpiDepth) * kNumActRecCells;
893 void FuncEmitter::addUserAttribute(const StringData* name, TypedValue tv) {
894 m_userAttributes[name] = tv;
897 void FuncEmitter::commit(RepoTxn& txn) const {
898 Repo& repo = Repo::get();
899 FuncRepoProxy& frp = repo.frp();
900 int repoId = m_ue.repoId();
901 int64_t usn = m_ue.sn();
903 frp.insertFunc(repoId)
904 .insert(*this, txn, usn, m_sn, m_pce ? m_pce->id() : -1, m_name, m_top);
907 Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
908 Attr attrs = m_attrs;
909 if (attrs & AttrPersistent &&
910 ((RuntimeOption::EvalJitEnableRenameFunction &&
911 !isdigit(m_name->data()[0])) ||
912 (!RuntimeOption::RepoAuthoritative && SystemLib::s_inited))) {
913 attrs = Attr(attrs & ~AttrPersistent);
915 if (RuntimeOption::EvalJitEnableRenameFunction &&
916 !m_name->empty() &&
917 !Func::isSpecial(m_name) &&
918 !m_isClosureBody &&
919 !m_isGenerator) {
920 // intercepted functions need to pass all args through
921 // to the interceptee
922 attrs = attrs | AttrMayUseVV;
925 if (!m_containsCalls) attrs = Attr(attrs | AttrPhpLeafFn);
927 Func* f = (m_pce == nullptr)
928 ? m_ue.newFunc(this, unit, m_id, m_line1, m_line2, m_base,
929 m_past, m_name, attrs, m_top, m_docComment,
930 m_params.size(), m_isClosureBody | m_isGeneratorFromClosure,
931 m_isGenerator)
932 : m_ue.newFunc(this, unit, preClass, m_line1, m_line2, m_base,
933 m_past, m_name, attrs, m_top, m_docComment,
934 m_params.size(), m_isClosureBody | m_isGeneratorFromClosure,
935 m_isGenerator);
936 f->shared()->m_info = m_info;
937 f->shared()->m_returnType = m_returnType;
938 std::vector<Func::ParamInfo> pBuilder;
939 for (unsigned i = 0; i < m_params.size(); ++i) {
940 Func::ParamInfo pi;
941 pi.setFuncletOff(m_params[i].funcletOff());
942 pi.setDefaultValue(m_params[i].defaultValue());
943 pi.setPhpCode(m_params[i].phpCode());
944 pi.setTypeConstraint(m_params[i].typeConstraint());
945 pi.setUserAttributes(m_params[i].userAttributes());
946 pi.setBuiltinType(m_params[i].builtinType());
947 pi.setUserType(m_params[i].userType());
948 f->appendParam(m_params[i].ref(), pi, pBuilder);
950 if (!m_params.size()) {
951 assert(!f->m_refBitVal && !f->shared()->m_refBitPtr);
952 f->m_refBitVal = attrs & AttrVariadicByRef ? -1uLL : 0uLL;
955 f->shared()->m_params = pBuilder;
956 f->shared()->m_localNames.create(m_localNames);
957 f->shared()->m_numLocals = m_numLocals;
958 f->shared()->m_numIterators = m_numIterators;
959 f->m_maxStackCells = m_maxStackCells;
960 assert(m_maxStackCells > 0 && "You probably didn't set m_maxStackCells");
961 f->shared()->m_staticVars = m_staticVars;
962 f->shared()->m_ehtab = m_ehtab;
963 f->shared()->m_fpitab = m_fpitab;
964 f->shared()->m_isClosureBody = m_isClosureBody;
965 f->shared()->m_isGenerator = m_isGenerator;
966 f->shared()->m_isGeneratorFromClosure = m_isGeneratorFromClosure;
967 f->shared()->m_hasGeneratorAsBody = m_hasGeneratorAsBody;
968 f->shared()->m_userAttributes = m_userAttributes;
969 f->shared()->m_builtinFuncPtr = m_builtinFuncPtr;
970 f->shared()->m_nativeFuncPtr = m_nativeFuncPtr;
971 f->shared()->m_retTypeConstraint = m_retTypeConstraint;
972 f->shared()->m_originalFilename = m_originalFilename;
973 return f;
976 void FuncEmitter::setBuiltinFunc(const ClassInfo::MethodInfo* info,
977 BuiltinFunction bif, BuiltinFunction nif,
978 Offset base) {
979 assert(info);
980 assert(bif);
981 m_info = info;
982 m_builtinFuncPtr = bif;
983 m_nativeFuncPtr = nif;
984 m_base = base;
985 m_top = true;
986 m_docComment = StringData::GetStaticString(info->docComment);
987 m_line1 = 0;
988 m_line2 = 0;
989 m_attrs = AttrBuiltin | AttrSkipFrame;
990 // TODO: Task #1137917: See if we can avoid marking most builtins with
991 // "MayUseVV" and still make things work
992 m_attrs = m_attrs | AttrMayUseVV;
993 if (info->attribute & (ClassInfo::RefVariableArguments |
994 ClassInfo::MixedVariableArguments)) {
995 m_attrs = m_attrs | AttrVariadicByRef;
997 if (info->attribute & ClassInfo::IsReference) {
998 m_attrs = m_attrs | AttrReference;
1000 if (info->attribute & ClassInfo::NoInjection) {
1001 m_attrs = m_attrs | AttrNoInjection;
1003 if (pce()) {
1004 if (info->attribute & ClassInfo::IsStatic) {
1005 m_attrs = m_attrs | AttrStatic;
1007 if (info->attribute & ClassInfo::IsFinal) {
1008 m_attrs = m_attrs | AttrFinal;
1010 if (info->attribute & ClassInfo::IsAbstract) {
1011 m_attrs = m_attrs | AttrAbstract;
1013 if (info->attribute & ClassInfo::IsPrivate) {
1014 m_attrs = m_attrs | AttrPrivate;
1015 } else if (info->attribute & ClassInfo::IsProtected) {
1016 m_attrs = m_attrs | AttrProtected;
1017 } else {
1018 m_attrs = m_attrs | AttrPublic;
1020 } else if (info->attribute & ClassInfo::AllowOverride) {
1021 m_attrs = m_attrs | AttrAllowOverride;
1024 m_returnType = info->returnType;
1025 for (unsigned i = 0; i < info->parameters.size(); ++i) {
1026 // For builtin only, we use a dummy ParamInfo
1027 FuncEmitter::ParamInfo pi;
1028 pi.setRef((bool)(info->parameters[i]->attribute & ClassInfo::IsReference));
1029 pi.setBuiltinType(info->parameters[i]->argType);
1030 appendParam(StringData::GetStaticString(info->parameters[i]->name), pi);
1034 template<class SerDe>
1035 void FuncEmitter::serdeMetaData(SerDe& sd) {
1036 // NOTE: name, top, and a few other fields currently serialized
1037 // outside of this.
1038 sd(m_line1)
1039 (m_line2)
1040 (m_base)
1041 (m_past)
1042 (m_attrs)
1043 (m_returnType)
1044 (m_docComment)
1045 (m_numLocals)
1046 (m_numIterators)
1047 (m_maxStackCells)
1048 (m_isClosureBody)
1049 (m_isGenerator)
1050 (m_isGeneratorFromClosure)
1051 (m_hasGeneratorAsBody)
1052 (m_containsCalls)
1054 (m_params)
1055 (m_localNames)
1056 (m_staticVars)
1057 (m_ehtab)
1058 (m_fpitab)
1059 (m_userAttributes)
1060 (m_retTypeConstraint)
1061 (m_originalFilename)
1065 //=============================================================================
1066 // FuncRepoProxy.
1068 FuncRepoProxy::FuncRepoProxy(Repo& repo)
1069 : RepoProxy(repo)
1070 #define FRP_OP(c, o) \
1071 , m_##o##Local(repo, RepoIdLocal), m_##o##Central(repo, RepoIdCentral)
1072 FRP_OPS
1073 #undef FRP_OP
1075 #define FRP_OP(c, o) \
1076 m_##o[RepoIdLocal] = &m_##o##Local; \
1077 m_##o[RepoIdCentral] = &m_##o##Central;
1078 FRP_OPS
1079 #undef FRP_OP
1082 FuncRepoProxy::~FuncRepoProxy() {
1085 void FuncRepoProxy::createSchema(int repoId, RepoTxn& txn) {
1086 std::stringstream ssCreate;
1087 ssCreate << "CREATE TABLE " << m_repo.table(repoId, "Func")
1088 << "(unitSn INTEGER, funcSn INTEGER, preClassId INTEGER,"
1089 " name TEXT, top INTEGER,"
1090 " extraData BLOB,"
1091 " PRIMARY KEY (unitSn, funcSn));";
1092 txn.exec(ssCreate.str());
1095 void FuncRepoProxy::InsertFuncStmt
1096 ::insert(const FuncEmitter& fe,
1097 RepoTxn& txn, int64_t unitSn, int funcSn,
1098 Id preClassId, const StringData* name,
1099 bool top) {
1100 if (!prepared()) {
1101 std::stringstream ssInsert;
1102 ssInsert << "INSERT INTO " << m_repo.table(m_repoId, "Func")
1103 << " VALUES(@unitSn, @funcSn, @preClassId, @name, "
1104 " @top, @extraData);";
1105 txn.prepare(*this, ssInsert.str());
1108 BlobEncoder extraBlob;
1109 RepoTxnQuery query(txn, *this);
1110 query.bindInt64("@unitSn", unitSn);
1111 query.bindInt("@funcSn", funcSn);
1112 query.bindId("@preClassId", preClassId);
1113 query.bindStaticString("@name", name);
1114 query.bindBool("@top", top);
1115 const_cast<FuncEmitter&>(fe).serdeMetaData(extraBlob);
1116 query.bindBlob("@extraData", extraBlob, /* static */ true);
1117 query.exec();
1120 void FuncRepoProxy::GetFuncsStmt
1121 ::get(UnitEmitter& ue) {
1122 RepoTxn txn(m_repo);
1123 if (!prepared()) {
1124 std::stringstream ssSelect;
1125 ssSelect << "SELECT funcSn,preClassId,name,top,extraData "
1126 "FROM "
1127 << m_repo.table(m_repoId, "Func")
1128 << " WHERE unitSn == @unitSn ORDER BY funcSn ASC;";
1129 txn.prepare(*this, ssSelect.str());
1131 RepoTxnQuery query(txn, *this);
1132 query.bindInt64("@unitSn", ue.sn());
1133 do {
1134 query.step();
1135 if (query.row()) {
1136 int funcSn; /**/ query.getInt(0, funcSn);
1137 Id preClassId; /**/ query.getId(1, preClassId);
1138 StringData* name; /**/ query.getStaticString(2, name);
1139 bool top; /**/ query.getBool(3, top);
1140 BlobDecoder extraBlob = /**/ query.getBlob(4);
1142 FuncEmitter* fe;
1143 if (preClassId < 0) {
1144 fe = ue.newFuncEmitter(name, top);
1145 } else {
1146 PreClassEmitter* pce = ue.pce(preClassId);
1147 fe = ue.newMethodEmitter(name, pce);
1148 bool added UNUSED = pce->addMethod(fe);
1149 assert(added);
1151 assert(fe->sn() == funcSn);
1152 fe->setTop(top);
1153 fe->serdeMetaData(extraBlob);
1154 fe->finish(fe->past(), true);
1155 ue.recordFunction(fe);
1157 } while (!query.done());
1158 txn.commit();
1161 } // HPHP::VM