2 +----------------------------------------------------------------------+
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"
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"
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 //=============================================================================
46 static void decl_incompat(const PreClass
* implementor
,
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
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
);
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());
101 m_fullName
= StringData::GetStaticString(
102 std::string(m_cls
->name()->data()) + "::" + m_name
->data());
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
);
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;
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
148 m_attrs
= m_attrs
| AttrNoInjection
;
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;
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
,
186 , m_namedEntity(nullptr)
187 , m_refBitVec(nullptr)
192 , m_funcId(InvalidFuncId
)
193 , m_hasPrivateAncestor(false)
195 m_shared
= new SharedData(nullptr, id
, base
, past
, line1
, line2
,
197 init(numParams
, isGenerator
);
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
,
209 , m_namedEntity(nullptr)
210 , m_refBitVec(nullptr)
215 , m_funcId(InvalidFuncId
)
216 , m_hasPrivateAncestor(false)
219 m_shared
= new SharedData(preClass
, id
, base
, past
, line1
, line2
,
221 init(numParams
, isGenerator
);
225 if (m_fullName
!= nullptr && m_maybeIntercepted
!= -1) {
226 unregister_intercept_flag(fullNameRef(), &m_maybeIntercepted
);
234 void Func::destroy(Func
* func
) {
236 if (func
->isClosureBody() || func
->isGeneratorFromClosure()) {
237 Func
** startOfFunc
= (Func
**) mem
;
238 mem
= startOfFunc
- 1; // move back by a pointer
244 Func
* Func::clone() const {
245 Func
* f
= new (allocFuncMem(
248 isClosureBody() || isGeneratorFromClosure()
250 f
->initPrologues(m_numParams
, isGenerator());
251 f
->m_funcId
= InvalidFuncId
;
255 const Func
* Func::cloneAndSetClass(Class
* cls
) const {
256 if (const Func
* ret
= findCachedClone(cls
)) {
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
)) {
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();
274 nextFunc
= &nextFunc
[0]->nextClonedClosure();
276 *nextFunc
= clonedFunc
;
281 const Func
* Func::findCachedClone(Class
* cls
) const {
282 const Func
* nextFunc
= this;
284 if (nextFunc
->cls() == cls
) {
287 nextFunc
= nextFunc
->nextClonedClosure();
292 void Func::rename(const StringData
* name
) {
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
) {
314 const EHEnt
* Func::findEH(Offset o
) const {
315 assert(o
>= base() && o
< past());
316 const EHEnt
* eh
= nullptr;
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
) {
328 const FPIEnt
* Func::findFPI(Offset o
) const {
329 assert(o
>= base() && o
< past());
330 const FPIEnt
* fe
= nullptr;
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
) {
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];
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
) {
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
) {
381 if (isUnique() && RuntimeOption::RepoAuthoritative
) {
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
))) {
399 int qword
= arg
/ kBitsPerQword
;
400 int bit
= arg
% kBitsPerQword
;
401 bool retval
= arg
< m_numParams
&& (m_refBitVec
[qword
] & (1ull << bit
)) != 0;
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
)) {
414 int qword
= arg
/ kBitsPerQword
;
415 int bit
= arg
% kBitsPerQword
;
416 bool retval
= arg
< m_numParams
&& (m_refBitVec
[qword
] & (1ull << bit
)) != 0;
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) {
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();
465 out
<< preClass()->name()->data() << "::" << m_name
->data();
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
<< ")";
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();
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");
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
;
508 out
<< " to " << it
->m_fault
;
510 if (it
->m_parentIndex
!= -1) {
511 out
<< " parentIndex " << it
->m_parentIndex
;
517 HphpArray
* Func::getStaticLocals() const {
518 return g_vmContext
->getFuncStaticCtx(this);
521 void Func::getFuncInfo(ClassInfo::MethodInfo
* mi
) const {
523 if (info() != nullptr) {
524 // Very large operator=() invocation.
526 // Deep copy the vectors of mi-owned pointers.
527 cloneMembers(mi
->parameters
);
528 cloneMembers(mi
->staticVariables
);
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.
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
;
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();
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
;
564 attr
|= ClassInfo::IsReference
;
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()) {
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());
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
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();
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() {
652 void Func::SharedData::atomicRelease() {
656 Func
** Func::getCachedAddr() {
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
);
669 return Unit::lookupFunc(name
);
673 //=============================================================================
676 FuncEmitter::FuncEmitter(UnitEmitter
& ue
, int sn
, Id id
, const StringData
* n
)
683 , m_numUnnamedLocals(0)
684 , m_activeUnnamedLocals(0)
686 , m_nextFreeIterator(0)
687 , m_retTypeConstraint(nullptr)
688 , m_returnType(KindOfInvalid
)
690 , m_isClosureBody(false)
691 , m_isGenerator(false)
692 , m_isGeneratorFromClosure(false)
693 , m_hasGeneratorAsBody(false)
694 , m_containsCalls(false)
696 , m_builtinFuncPtr(nullptr)
697 , m_originalFilename(nullptr)
700 FuncEmitter::FuncEmitter(UnitEmitter
& ue
, int sn
, const StringData
* n
,
701 PreClassEmitter
* pce
)
707 , m_numUnnamedLocals(0)
708 , m_activeUnnamedLocals(0)
710 , m_nextFreeIterator(0)
711 , m_retTypeConstraint(nullptr)
712 , m_returnType(KindOfInvalid
)
714 , m_isClosureBody(false)
715 , m_isGenerator(false)
716 , m_isGeneratorFromClosure(false)
717 , m_hasGeneratorAsBody(false)
718 , m_containsCalls(false)
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
) {
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
) {
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
) {
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);
771 if (m_localNames
.find(name
) == m_localNames
.end()) {
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
;
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
) {
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
);
833 * Ordering on EHEnts where e1 < e2 iff
835 * a) e1 and e2 do not overlap, and e1 comes first
837 * c) e1 and e2 have the same region, but e1 is a Catch funclet and
838 * e2 is a Fault funclet.
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
;
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;
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
&&
917 !Func::isSpecial(m_name
) &&
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
,
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
,
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
) {
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
;
971 void FuncEmitter::setBuiltinFunc(const ClassInfo::MethodInfo
* info
,
972 BuiltinFunction bif
, BuiltinFunction nif
,
977 m_builtinFuncPtr
= bif
;
978 m_nativeFuncPtr
= nif
;
981 m_docComment
= StringData::GetStaticString(info
->docComment
);
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
;
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
;
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
1045 (m_isGeneratorFromClosure
)
1046 (m_hasGeneratorAsBody
)
1055 (m_retTypeConstraint
)
1056 (m_originalFilename
)
1060 //=============================================================================
1063 FuncRepoProxy::FuncRepoProxy(Repo
& repo
)
1065 #define FRP_OP(c, o) \
1066 , m_##o##Local(repo, RepoIdLocal), m_##o##Central(repo, RepoIdCentral)
1070 #define FRP_OP(c, o) \
1071 m_##o[RepoIdLocal] = &m_##o##Local; \
1072 m_##o[RepoIdCentral] = &m_##o##Central;
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,"
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
,
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);
1115 void FuncRepoProxy::GetFuncsStmt
1116 ::get(UnitEmitter
& ue
) {
1117 RepoTxn
txn(m_repo
);
1119 std::stringstream ssSelect
;
1120 ssSelect
<< "SELECT funcSn,preClassId,name,top,extraData "
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());
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);
1138 if (preClassId
< 0) {
1139 fe
= ue
.newFuncEmitter(name
, top
);
1141 PreClassEmitter
* pce
= ue
.pce(preClassId
);
1142 fe
= ue
.newMethodEmitter(name
, pce
);
1143 bool added UNUSED
= pce
->addMethod(fe
);
1146 assert(fe
->sn() == funcSn
);
1148 fe
->serdeMetaData(extraBlob
);
1149 fe
->finish(fe
->past(), true);
1150 ue
.recordFunction(fe
);
1152 } while (!query
.done());