Move runtime/eval/runtime/file_repository.* to runtime/base
[hiphop-php.git] / hphp / runtime / vm / func.cpp
bloba347f3348a9069acf32eda941f1a5b93c6e9c86d
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"
36 namespace HPHP {
38 static const Trace::Module TRACEMOD = Trace::bcinterp;
39 const StringData* Func::s___call = StringData::GetStaticString("__call");
40 const StringData* Func::s___callStatic =
41 StringData::GetStaticString("__callStatic");
43 //=============================================================================
44 // Func.
46 static void decl_incompat(const PreClass* implementor,
47 const Func* imeth) {
48 const char* name = imeth->name()->data();
49 raise_error("Declaration of %s::%s() must be compatible with "
50 "that of %s::%s()", implementor->name()->data(), name,
51 imeth->cls()->preClass()->name()->data(), name);
54 // Check compatibility vs interface and abstract declarations
55 void Func::parametersCompat(const PreClass* preClass, const Func* imeth) const {
56 const Func::ParamInfoVec& params = this->params();
57 const Func::ParamInfoVec& iparams = imeth->params();
58 // Verify that meth has at least as many parameters as imeth.
59 if ((params.size() < iparams.size())) {
60 decl_incompat(preClass, imeth);
62 // Verify that the typehints for meth's parameters are compatible with
63 // imeth's corresponding parameter typehints.
64 unsigned firstOptional = 0;
65 for (unsigned i = 0; i < iparams.size(); ++i) {
66 if (!params[i].typeConstraint().compat(iparams[i].typeConstraint())) {
67 decl_incompat(preClass, imeth);
69 if (!iparams[i].hasDefaultValue()) {
70 // The leftmost of imeth's contiguous trailing optional parameters
71 // must start somewhere to the right of this parameter.
72 firstOptional = i + 1;
75 // Verify that meth provides defaults, starting with the parameter that
76 // corresponds to the leftmost of imeth's contiguous trailing optional
77 // parameters.
78 for (unsigned i = firstOptional; i < params.size(); ++i) {
79 if (!params[i].hasDefaultValue()) {
80 decl_incompat(preClass, imeth);
85 static FuncId s_nextFuncId = 0;
87 void Func::setFuncId(FuncId id) {
88 assert(m_funcId == InvalidFuncId);
89 assert(id != InvalidFuncId);
90 m_funcId = id;
93 void Func::setNewFuncId() {
94 assert(m_funcId == InvalidFuncId);
95 m_funcId = static_cast<FuncId>(__sync_fetch_and_add(&s_nextFuncId, 1));
98 void Func::setFullName() {
99 assert(m_name->isStatic());
100 if (m_cls) {
101 m_fullName = StringData::GetStaticString(
102 std::string(m_cls->name()->data()) + "::" + m_name->data());
103 } else {
104 m_fullName = m_name;
105 m_namedEntity = Unit::GetNamedEntity(m_name);
107 if (RuntimeOption::DynamicInvokeFunctions.size()) {
108 if (RuntimeOption::DynamicInvokeFunctions.find(m_fullName->data()) !=
109 RuntimeOption::DynamicInvokeFunctions.end()) {
110 m_attrs = Attr(m_attrs | AttrDynamicInvoke);
115 void Func::initPrologues(int numParams, bool isGenerator) {
116 m_funcBody = (TCA)HPHP::Transl::funcBodyHelperThunk;
118 int maxNumPrologues = Func::getMaxNumPrologues(numParams);
119 int numPrologues =
120 maxNumPrologues > kNumFixedPrologues ? maxNumPrologues
121 : kNumFixedPrologues;
123 TRACE(2, "initPrologues func %p %d\n", this, numPrologues);
124 for (int i = 0; i < numPrologues; i++) {
125 m_prologueTable[i] = (TCA)HPHP::Transl::fcallHelperThunk;
129 void Func::init(int numParams, bool isGenerator) {
130 // For methods, we defer setting the full name until m_cls is initialized
131 m_maybeIntercepted = -1;
132 if (!preClass()) {
133 setNewFuncId();
134 setFullName();
135 } else {
136 m_fullName = 0;
138 if (isSpecial(m_name)) {
140 * i) We dont want these compiler generated functions to
141 * appear in backtraces.
143 * ii) 86sinit and 86pinit construct NameValueTableWrappers
144 * on the stack. So we MUST NOT allow those to leak into
145 * the backtrace (since the backtrace will outlive the
146 * variables).
148 m_attrs = m_attrs | AttrNoInjection;
150 #ifdef DEBUG
151 m_magic = kMagic;
152 #endif
153 assert(m_name);
154 initPrologues(numParams, isGenerator);
157 void* Func::allocFuncMem(
158 const StringData* name, int numParams, bool needsNextClonedClosure) {
159 int maxNumPrologues = Func::getMaxNumPrologues(numParams);
160 int numExtraPrologues =
161 maxNumPrologues > kNumFixedPrologues ?
162 maxNumPrologues - kNumFixedPrologues :
164 size_t funcSize = sizeof(Func) + numExtraPrologues * sizeof(unsigned char*);
165 if (needsNextClonedClosure) {
166 funcSize += sizeof(Func*);
168 void* mem = Util::low_malloc(funcSize);
169 if (needsNextClonedClosure) {
170 // make room for nextClonedClosure to work
171 Func** startOfFunc = (Func**) mem;
172 *startOfFunc = nullptr;
173 return startOfFunc + 1;
175 return mem;
178 Func::Func(Unit& unit, Id id, int line1, int line2,
179 Offset base, Offset past, const StringData* name,
180 Attr attrs, bool top, const StringData* docComment, int numParams,
181 bool isGenerator)
182 : m_unit(&unit)
183 , m_cls(nullptr)
184 , m_baseCls(nullptr)
185 , m_name(name)
186 , m_namedEntity(nullptr)
187 , m_refBitVec(nullptr)
188 , m_cachedOffset(0)
189 , m_maxStackCells(0)
190 , m_numParams(0)
191 , m_attrs(attrs)
192 , m_funcId(InvalidFuncId)
193 , m_hasPrivateAncestor(false)
195 m_shared = new SharedData(nullptr, id, base, past, line1, line2,
196 top, docComment);
197 init(numParams, isGenerator);
200 // Class method
201 Func::Func(Unit& unit, PreClass* preClass, int line1, int line2, Offset base,
202 Offset past, const StringData* name, Attr attrs,
203 bool top, const StringData* docComment, int numParams,
204 bool isGenerator)
205 : m_unit(&unit)
206 , m_cls(nullptr)
207 , m_baseCls(nullptr)
208 , m_name(name)
209 , m_namedEntity(nullptr)
210 , m_refBitVec(nullptr)
211 , m_cachedOffset(0)
212 , m_maxStackCells(0)
213 , m_numParams(0)
214 , m_attrs(attrs)
215 , m_funcId(InvalidFuncId)
216 , m_hasPrivateAncestor(false)
218 Id id = -1;
219 m_shared = new SharedData(preClass, id, base, past, line1, line2,
220 top, docComment);
221 init(numParams, isGenerator);
224 Func::~Func() {
225 if (m_fullName != nullptr && m_maybeIntercepted != -1) {
226 unregister_intercept_flag(fullNameRef(), &m_maybeIntercepted);
228 #ifdef DEBUG
229 validate();
230 m_magic = ~m_magic;
231 #endif
234 void Func::destroy(Func* func) {
235 void* mem = func;
236 if (func->isClosureBody() || func->isGeneratorFromClosure()) {
237 Func** startOfFunc = (Func**) mem;
238 mem = startOfFunc - 1; // move back by a pointer
240 func->~Func();
241 Util::low_free(mem);
244 Func* Func::clone() const {
245 Func* f = new (allocFuncMem(
246 m_name,
247 m_numParams,
248 isClosureBody() || isGeneratorFromClosure()
249 )) Func(*this);
250 f->initPrologues(m_numParams, isGenerator());
251 f->m_funcId = InvalidFuncId;
252 return f;
255 const Func* Func::cloneAndSetClass(Class* cls) const {
256 if (const Func* ret = findCachedClone(cls)) {
257 return ret;
260 static Mutex s_clonedFuncListMutex;
261 Lock l(s_clonedFuncListMutex);
262 // Check again now that I'm the writer
263 if (const Func* ret = findCachedClone(cls)) {
264 return ret;
267 Func* clonedFunc = clone();
268 clonedFunc->setNewFuncId();
269 clonedFunc->setCls(cls);
271 // Save it so we don't have to keep cloning it and retranslating
272 Func** nextFunc = &this->nextClonedClosure();
273 while (*nextFunc) {
274 nextFunc = &nextFunc[0]->nextClonedClosure();
276 *nextFunc = clonedFunc;
278 return clonedFunc;
281 const Func* Func::findCachedClone(Class* cls) const {
282 const Func* nextFunc = this;
283 while (nextFunc) {
284 if (nextFunc->cls() == cls) {
285 return nextFunc;
287 nextFunc = nextFunc->nextClonedClosure();
289 return nullptr;
292 void Func::rename(const StringData* name) {
293 m_name = name;
294 setFullName();
295 // load the renamed function
296 Unit::loadFunc(this);
299 bool Func::checkIterScope(Offset o, Id iterId, bool& itRef) const {
300 const EHEntVec& ehtab = shared()->m_ehtab;
301 assert(o >= base() && o < past());
302 for (unsigned i = 0, n = ehtab.size(); i < n; i++) {
303 const EHEnt* eh = &ehtab[i];
304 if (eh->m_ehtype == EHEnt::EHType_Fault &&
305 eh->m_base <= o && o < eh->m_past &&
306 eh->m_iterId == iterId) {
307 itRef = eh->m_itRef;
308 return true;
311 return false;
314 const EHEnt* Func::findEH(Offset o) const {
315 assert(o >= base() && o < past());
316 const EHEnt* eh = nullptr;
317 unsigned int i;
319 const EHEntVec& ehtab = shared()->m_ehtab;
320 for (i = 0; i < ehtab.size(); i++) {
321 if (ehtab[i].m_base <= o && o < ehtab[i].m_past) {
322 eh = &ehtab[i];
325 return eh;
328 const FPIEnt* Func::findFPI(Offset o) const {
329 assert(o >= base() && o < past());
330 const FPIEnt* fe = nullptr;
331 unsigned int i;
333 const FPIEntVec& fpitab = shared()->m_fpitab;
334 for (i = 0; i < fpitab.size(); i++) {
336 * We consider the "FCall" instruction part of the FPI region, but
337 * the corresponding push is not considered part of it. (This
338 * means all offsets in the FPI region will have the partial
339 * ActRec on the stack.)
341 if (fpitab[i].m_fpushOff < o && o <= fpitab[i].m_fcallOff) {
342 fe = &fpitab[i];
345 return fe;
348 const FPIEnt* Func::findPrecedingFPI(Offset o) const {
349 assert(o >= base() && o < past());
350 const FPIEntVec& fpitab = shared()->m_fpitab;
351 assert(fpitab.size());
352 const FPIEnt* fe = &fpitab[0];
353 unsigned int i;
354 for (i = 1; i < fpitab.size(); i++) {
355 const FPIEnt* cur = &fpitab[i];
356 if (o > cur->m_fcallOff &&
357 fe->m_fcallOff < cur->m_fcallOff) {
358 fe = cur;
361 assert(fe);
362 return fe;
365 bool Func::isClonedClosure() const {
366 if (!isClosureBody()) return false;
367 if (!cls()) return true;
368 return cls()->lookupMethod(name()) != this;
371 bool Func::isNameBindingImmutable(const Unit* fromUnit) const {
372 if (RuntimeOption::EvalJitEnableRenameFunction ||
373 m_attrs & AttrDynamicInvoke) {
374 return false;
377 if (isBuiltin()) {
378 return true;
381 if (isUnique() && RuntimeOption::RepoAuthoritative) {
382 return true;
385 // Defined at top level, in the same unit as the caller. This precludes
386 // conditionally defined functions and cross-module calls -- both phenomena
387 // can change name->Func mappings during the lifetime of a TC.
388 return top() && (fromUnit == m_unit);
391 bool Func::byRef(int32_t arg) const {
392 // Super special case. A handful of builtins are varargs functions where the
393 // (not formally declared) varargs are pass-by-reference. psychedelic-kitten
394 if (arg >= m_numParams && info() &&
395 (info()->attribute & (ClassInfo::RefVariableArguments |
396 ClassInfo::MixedVariableArguments))) {
397 return true;
399 int qword = arg / kBitsPerQword;
400 int bit = arg % kBitsPerQword;
401 bool retval = arg < m_numParams && (m_refBitVec[qword] & (1ull << bit)) != 0;
402 return retval;
405 bool Func::mustBeRef(int32_t arg) const {
406 // return true if the argument is required to be a reference
407 // (and thus should be an lvalue)
408 if (arg >= m_numParams && info() &&
409 ((info()->attribute & (ClassInfo::RefVariableArguments |
410 ClassInfo::MixedVariableArguments)) ==
411 ClassInfo::RefVariableArguments)) {
412 return true;
414 int qword = arg / kBitsPerQword;
415 int bit = arg % kBitsPerQword;
416 bool retval = arg < m_numParams && (m_refBitVec[qword] & (1ull << bit)) != 0;
417 return retval;
420 void Func::appendParam(bool ref, const Func::ParamInfo& info,
421 std::vector<ParamInfo>& pBuilder) {
422 int qword = m_numParams / kBitsPerQword;
423 int bit = m_numParams % kBitsPerQword;
424 // Grow args, if necessary.
425 if ((m_numParams++ & (kBitsPerQword - 1)) == 0) {
426 assert(shared()->m_refBitVec == m_refBitVec);
427 shared()->m_refBitVec = m_refBitVec = (uint64_t*)
428 realloc(shared()->m_refBitVec,
429 // E.g., 65th m_numParams -> 2 qwords
430 (1 + m_numParams / kBitsPerQword) * sizeof(uint64_t));
432 // The new word is either zerod or set to 1, depending on whether
433 // we are one of the special builtins that takes variadic
434 // reference arguments. This is for use in the translator.
435 shared()->m_refBitVec[m_numParams / kBitsPerQword] =
436 (m_attrs & AttrVariadicByRef) ? -1ull : 0;
438 assert(!!(shared()->m_refBitVec[qword] & (uint64_t(1) << bit)) ==
439 !!(m_attrs & AttrVariadicByRef));
440 shared()->m_refBitVec[qword] &= ~(1ull << bit);
441 shared()->m_refBitVec[qword] |= uint64_t(ref) << bit;
442 pBuilder.push_back(info);
445 Id Func::lookupVarId(const StringData* name) const {
446 assert(name != nullptr);
447 return shared()->m_localNames.findIndex(name);
450 void Func::prettyPrint(std::ostream& out) const {
451 if (isPseudoMain()) {
452 out << "Pseudo-main";
453 } else if (preClass() != nullptr) {
454 out << "Method ";
455 if (m_attrs & AttrStatic) { out << "static "; }
456 if (m_attrs & AttrPublic) { out << "public "; }
457 if (m_attrs & AttrProtected) { out << "protected "; }
458 if (m_attrs & AttrPrivate) { out << "private "; }
459 if (m_attrs & AttrAbstract) { out << "abstract "; }
460 if (m_attrs & AttrFinal) { out << "final "; }
461 if (m_attrs & AttrPhpLeafFn) { out << "(leaf) "; }
462 if (cls() != nullptr) {
463 out << fullName()->data();
464 } else {
465 out << preClass()->name()->data() << "::" << m_name->data();
467 } else {
468 out << "Function " << m_name->data();
471 if (m_attrs & AttrHot) out << " (hot)";
473 out << " at " << base();
474 if (shared()->m_id != -1) {
475 out << " (ID " << shared()->m_id << ")";
477 out << std::endl;
478 const ParamInfoVec& params = shared()->m_params;
479 for (uint i = 0; i < params.size(); ++i) {
480 if (params[i].funcletOff() != InvalidAbsoluteOffset) {
481 out << " DV for parameter " << i << " at " << params[i].funcletOff();
482 if (params[i].phpCode()) {
483 out << " = " << params[i].phpCode()->data();
485 out << std::endl;
488 const EHEntVec& ehtab = shared()->m_ehtab;
489 for (EHEntVec::const_iterator it = ehtab.begin(); it != ehtab.end(); ++it) {
490 bool catcher = it->m_ehtype == EHEnt::EHType_Catch;
491 out << " EH " << (catcher ? "Catch" : "Fault") << " for " <<
492 it->m_base << ":" << it->m_past;
493 if (it->m_parentIndex != -1) {
494 out << " outer EH " << it->m_parentIndex;
496 if (it->m_iterId != -1) {
497 out << " iterId " << it->m_iterId;
498 out << " itRef " << (it->m_itRef ? "true" : "false");
500 if (catcher) {
501 out << std::endl;
502 for (EHEnt::CatchVec::const_iterator it2 = it->m_catches.begin();
503 it2 != it->m_catches.end(); ++it2) {
504 out << " Handle " << m_unit->lookupLitstrId(it2->first)->data()
505 << " at " << it2->second;
507 } else {
508 out << " to " << it->m_fault;
510 if (it->m_parentIndex != -1) {
511 out << " parentIndex " << it->m_parentIndex;
513 out << std::endl;
517 HphpArray* Func::getStaticLocals() const {
518 return g_vmContext->getFuncStaticCtx(this);
521 void Func::getFuncInfo(ClassInfo::MethodInfo* mi) const {
522 assert(mi);
523 if (info() != nullptr) {
524 // Very large operator=() invocation.
525 *mi = *info();
526 // Deep copy the vectors of mi-owned pointers.
527 cloneMembers(mi->parameters);
528 cloneMembers(mi->staticVariables);
529 } else {
530 // hphpc sets the ClassInfo::VariableArguments attribute if the method
531 // contains a call to func_get_arg, func_get_args, or func_num_args. We
532 // don't do this in the VM currently and hopefully we never will need to.
533 int attr = 0;
534 if (m_attrs & AttrReference) attr |= ClassInfo::IsReference;
535 if (m_attrs & AttrAbstract) attr |= ClassInfo::IsAbstract;
536 if (m_attrs & AttrFinal) attr |= ClassInfo::IsFinal;
537 if (m_attrs & AttrProtected) attr |= ClassInfo::IsProtected;
538 if (m_attrs & AttrPrivate) attr |= ClassInfo::IsPrivate;
539 if (m_attrs & AttrStatic) attr |= ClassInfo::IsStatic;
540 if (!(attr & ClassInfo::IsProtected || attr & ClassInfo::IsPrivate)) {
541 attr |= ClassInfo::IsPublic;
543 if (preClass() &&
544 (!strcasecmp(m_name->data(), "__construct") ||
545 (!(preClass()->attrs() & AttrTrait) &&
546 !strcasecmp(m_name->data(), preClass()->name()->data()) &&
547 !preClass()->hasMethod(String("__construct").get())))) {
548 attr |= ClassInfo::IsConstructor;
550 if (attr == 0) attr = ClassInfo::IsNothing;
551 mi->attribute = (ClassInfo::Attribute)attr;
552 mi->name = m_name->data();
553 mi->file = m_unit->filepath()->data();
554 mi->line1 = line1();
555 mi->line2 = line2();
556 if (docComment() && !docComment()->empty()) {
557 mi->docComment = docComment()->data();
559 // Get the parameter info
560 for (unsigned i = 0; i < unsigned(m_numParams); ++i) {
561 ClassInfo::ParameterInfo* pi = new ClassInfo::ParameterInfo;
562 attr = 0;
563 if (byRef(i)) {
564 attr |= ClassInfo::IsReference;
566 if (attr == 0) {
567 attr = ClassInfo::IsNothing;
569 const ParamInfoVec& params = shared()->m_params;
570 const ParamInfo& fpi = params[i];
571 pi->attribute = (ClassInfo::Attribute)attr;
572 pi->name = shared()->m_localNames[i]->data();
573 if (params.size() <= i || !fpi.hasDefaultValue()) {
574 pi->value = nullptr;
575 pi->valueText = "";
576 } else {
577 if (fpi.hasScalarDefaultValue()) {
578 // Most of the time the default value is scalar, so we can
579 // avoid evaling in the common case
580 pi->value = strdup(f_serialize(
581 tvAsVariant((TypedValue*)&fpi.defaultValue())).get()->data());
582 } else {
583 // Eval PHP code to get default value, and serialize the result. Note
584 // that access of undefined class constants can cause the eval() to
585 // fatal. Zend lets such fatals propagate, so don't bother catching
586 // exceptions here.
587 CVarRef v = g_vmContext->getEvaledArg(fpi.phpCode());
588 pi->value = strdup(f_serialize(v).get()->data());
590 // This is a raw char*, but its lifetime should be at least as long
591 // as the the Func*. At this writing, it's a merged anon string
592 // owned by ParamInfo.
593 pi->valueText = fpi.phpCode()->data();
595 pi->type = fpi.typeConstraint().exists() ?
596 fpi.typeConstraint().typeName()->data() : "";
597 for (UserAttributeMap::const_iterator it = fpi.userAttributes().begin();
598 it != fpi.userAttributes().end(); ++it) {
599 // convert the typedvalue to a cvarref and push into pi.
600 auto userAttr = new ClassInfo::UserAttributeInfo;
601 assert(it->first->isStatic());
602 userAttr->name = const_cast<StringData*>(it->first);
603 userAttr->setStaticValue(tvAsCVarRef(&it->second));
604 pi->userAttrs.push_back(userAttr);
606 mi->parameters.push_back(pi);
608 // XXX ConstantInfo is abused to store static variable metadata, and
609 // although ConstantInfo::callbacks provides a mechanism for registering
610 // callbacks, it does not pass enough information through for the callback
611 // functions to know the function context whence the callbacks came.
612 // Furthermore, the callback mechanism isn't employed in a fashion that
613 // would allow repeated introspection to reflect updated values.
614 // Supporting introspection of static variable values will require
615 // different plumbing than currently exists in ConstantInfo.
616 const SVInfoVec& staticVars = shared()->m_staticVars;
617 for (SVInfoVec::const_iterator it = staticVars.begin();
618 it != staticVars.end(); ++it) {
619 ClassInfo::ConstantInfo* ci = new ClassInfo::ConstantInfo;
620 ci->name = *(String*)(&(*it).name);
621 if ((*it).phpCode != nullptr) {
622 ci->valueLen = (*it).phpCode->size();
623 ci->valueText = (*it).phpCode->data();
624 } else {
625 ci->valueLen = 0;
626 ci->valueText = "";
629 mi->staticVariables.push_back(ci);
634 Func::SharedData::SharedData(PreClass* preClass, Id id,
635 Offset base, Offset past, int line1, int line2,
636 bool top, const StringData* docComment)
637 : m_preClass(preClass), m_id(id), m_base(base),
638 m_numLocals(0), m_numIterators(0),
639 m_past(past), m_line1(line1), m_line2(line2),
640 m_info(nullptr), m_refBitVec(nullptr), m_builtinFuncPtr(nullptr),
641 m_docComment(docComment), m_top(top), m_isClosureBody(false),
642 m_isGenerator(false), m_isGeneratorFromClosure(false),
643 m_hasGeneratorAsBody(false), m_originalFilename(nullptr) {
646 Func::SharedData::~SharedData() {
647 if (m_refBitVec) {
648 free(m_refBitVec);
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 f->shared()->m_params = pBuilder;
951 f->shared()->m_localNames.create(m_localNames);
952 f->shared()->m_numLocals = m_numLocals;
953 f->shared()->m_numIterators = m_numIterators;
954 f->m_maxStackCells = m_maxStackCells;
955 assert(m_maxStackCells > 0 && "You probably didn't set m_maxStackCells");
956 f->shared()->m_staticVars = m_staticVars;
957 f->shared()->m_ehtab = m_ehtab;
958 f->shared()->m_fpitab = m_fpitab;
959 f->shared()->m_isClosureBody = m_isClosureBody;
960 f->shared()->m_isGenerator = m_isGenerator;
961 f->shared()->m_isGeneratorFromClosure = m_isGeneratorFromClosure;
962 f->shared()->m_hasGeneratorAsBody = m_hasGeneratorAsBody;
963 f->shared()->m_userAttributes = m_userAttributes;
964 f->shared()->m_builtinFuncPtr = m_builtinFuncPtr;
965 f->shared()->m_nativeFuncPtr = m_nativeFuncPtr;
966 f->shared()->m_retTypeConstraint = m_retTypeConstraint;
967 f->shared()->m_originalFilename = m_originalFilename;
968 return f;
971 void FuncEmitter::setBuiltinFunc(const ClassInfo::MethodInfo* info,
972 BuiltinFunction bif, BuiltinFunction nif,
973 Offset base) {
974 assert(info);
975 assert(bif);
976 m_info = info;
977 m_builtinFuncPtr = bif;
978 m_nativeFuncPtr = nif;
979 m_base = base;
980 m_top = true;
981 m_docComment = StringData::GetStaticString(info->docComment);
982 m_line1 = 0;
983 m_line2 = 0;
984 m_attrs = AttrBuiltin | AttrSkipFrame;
985 // TODO: Task #1137917: See if we can avoid marking most builtins with
986 // "MayUseVV" and still make things work
987 m_attrs = m_attrs | AttrMayUseVV;
988 if (info->attribute & (ClassInfo::RefVariableArguments |
989 ClassInfo::MixedVariableArguments)) {
990 m_attrs = m_attrs | AttrVariadicByRef;
992 if (info->attribute & ClassInfo::IsReference) {
993 m_attrs = m_attrs | AttrReference;
995 if (info->attribute & ClassInfo::NoInjection) {
996 m_attrs = m_attrs | AttrNoInjection;
998 if (pce()) {
999 if (info->attribute & ClassInfo::IsStatic) {
1000 m_attrs = m_attrs | AttrStatic;
1002 if (info->attribute & ClassInfo::IsFinal) {
1003 m_attrs = m_attrs | AttrFinal;
1005 if (info->attribute & ClassInfo::IsAbstract) {
1006 m_attrs = m_attrs | AttrAbstract;
1008 if (info->attribute & ClassInfo::IsPrivate) {
1009 m_attrs = m_attrs | AttrPrivate;
1010 } else if (info->attribute & ClassInfo::IsProtected) {
1011 m_attrs = m_attrs | AttrProtected;
1012 } else {
1013 m_attrs = m_attrs | AttrPublic;
1015 } else if (info->attribute & ClassInfo::AllowOverride) {
1016 m_attrs = m_attrs | AttrAllowOverride;
1019 m_returnType = info->returnType;
1020 for (unsigned i = 0; i < info->parameters.size(); ++i) {
1021 // For builtin only, we use a dummy ParamInfo
1022 FuncEmitter::ParamInfo pi;
1023 pi.setRef((bool)(info->parameters[i]->attribute & ClassInfo::IsReference));
1024 pi.setBuiltinType(info->parameters[i]->argType);
1025 appendParam(StringData::GetStaticString(info->parameters[i]->name), pi);
1029 template<class SerDe>
1030 void FuncEmitter::serdeMetaData(SerDe& sd) {
1031 // NOTE: name, top, and a few other fields currently serialized
1032 // outside of this.
1033 sd(m_line1)
1034 (m_line2)
1035 (m_base)
1036 (m_past)
1037 (m_attrs)
1038 (m_returnType)
1039 (m_docComment)
1040 (m_numLocals)
1041 (m_numIterators)
1042 (m_maxStackCells)
1043 (m_isClosureBody)
1044 (m_isGenerator)
1045 (m_isGeneratorFromClosure)
1046 (m_hasGeneratorAsBody)
1047 (m_containsCalls)
1049 (m_params)
1050 (m_localNames)
1051 (m_staticVars)
1052 (m_ehtab)
1053 (m_fpitab)
1054 (m_userAttributes)
1055 (m_retTypeConstraint)
1056 (m_originalFilename)
1060 //=============================================================================
1061 // FuncRepoProxy.
1063 FuncRepoProxy::FuncRepoProxy(Repo& repo)
1064 : RepoProxy(repo)
1065 #define FRP_OP(c, o) \
1066 , m_##o##Local(repo, RepoIdLocal), m_##o##Central(repo, RepoIdCentral)
1067 FRP_OPS
1068 #undef FRP_OP
1070 #define FRP_OP(c, o) \
1071 m_##o[RepoIdLocal] = &m_##o##Local; \
1072 m_##o[RepoIdCentral] = &m_##o##Central;
1073 FRP_OPS
1074 #undef FRP_OP
1077 FuncRepoProxy::~FuncRepoProxy() {
1080 void FuncRepoProxy::createSchema(int repoId, RepoTxn& txn) {
1081 std::stringstream ssCreate;
1082 ssCreate << "CREATE TABLE " << m_repo.table(repoId, "Func")
1083 << "(unitSn INTEGER, funcSn INTEGER, preClassId INTEGER,"
1084 " name TEXT, top INTEGER,"
1085 " extraData BLOB,"
1086 " PRIMARY KEY (unitSn, funcSn));";
1087 txn.exec(ssCreate.str());
1090 void FuncRepoProxy::InsertFuncStmt
1091 ::insert(const FuncEmitter& fe,
1092 RepoTxn& txn, int64_t unitSn, int funcSn,
1093 Id preClassId, const StringData* name,
1094 bool top) {
1095 if (!prepared()) {
1096 std::stringstream ssInsert;
1097 ssInsert << "INSERT INTO " << m_repo.table(m_repoId, "Func")
1098 << " VALUES(@unitSn, @funcSn, @preClassId, @name, "
1099 " @top, @extraData);";
1100 txn.prepare(*this, ssInsert.str());
1103 BlobEncoder extraBlob;
1104 RepoTxnQuery query(txn, *this);
1105 query.bindInt64("@unitSn", unitSn);
1106 query.bindInt("@funcSn", funcSn);
1107 query.bindId("@preClassId", preClassId);
1108 query.bindStaticString("@name", name);
1109 query.bindBool("@top", top);
1110 const_cast<FuncEmitter&>(fe).serdeMetaData(extraBlob);
1111 query.bindBlob("@extraData", extraBlob, /* static */ true);
1112 query.exec();
1115 void FuncRepoProxy::GetFuncsStmt
1116 ::get(UnitEmitter& ue) {
1117 RepoTxn txn(m_repo);
1118 if (!prepared()) {
1119 std::stringstream ssSelect;
1120 ssSelect << "SELECT funcSn,preClassId,name,top,extraData "
1121 "FROM "
1122 << m_repo.table(m_repoId, "Func")
1123 << " WHERE unitSn == @unitSn ORDER BY funcSn ASC;";
1124 txn.prepare(*this, ssSelect.str());
1126 RepoTxnQuery query(txn, *this);
1127 query.bindInt64("@unitSn", ue.sn());
1128 do {
1129 query.step();
1130 if (query.row()) {
1131 int funcSn; /**/ query.getInt(0, funcSn);
1132 Id preClassId; /**/ query.getId(1, preClassId);
1133 StringData* name; /**/ query.getStaticString(2, name);
1134 bool top; /**/ query.getBool(3, top);
1135 BlobDecoder extraBlob = /**/ query.getBlob(4);
1137 FuncEmitter* fe;
1138 if (preClassId < 0) {
1139 fe = ue.newFuncEmitter(name, top);
1140 } else {
1141 PreClassEmitter* pce = ue.pce(preClassId);
1142 fe = ue.newMethodEmitter(name, pce);
1143 bool added UNUSED = pce->addMethod(fe);
1144 assert(added);
1146 assert(fe->sn() == funcSn);
1147 fe->setTop(top);
1148 fe->serdeMetaData(extraBlob);
1149 fe->finish(fe->past(), true);
1150 ue.recordFunction(fe);
1152 } while (!query.done());
1153 txn.commit();
1156 } // HPHP::VM