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"
35 #include "hphp/runtime/vm/bytecode.h"
40 const StringData
* Func::s___call
= StringData::GetStaticString("__call");
41 const StringData
* Func::s___callStatic
=
42 StringData::GetStaticString("__callStatic");
44 //=============================================================================
47 static void decl_incompat(const PreClass
* implementor
,
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
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
);
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());
102 m_fullName
= StringData::GetStaticString(
103 std::string(m_cls
->name()->data()) + "::" + m_name
->data());
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
);
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;
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
149 m_attrs
= m_attrs
| AttrNoInjection
;
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;
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
,
187 , m_namedEntity(nullptr)
193 , m_funcId(InvalidFuncId
)
194 , m_hasPrivateAncestor(false)
196 m_shared
= new SharedData(nullptr, id
, base
, past
, line1
, line2
,
198 init(numParams
, isGenerator
);
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
,
210 , m_namedEntity(nullptr)
216 , m_funcId(InvalidFuncId
)
217 , m_hasPrivateAncestor(false)
220 m_shared
= new SharedData(preClass
, id
, base
, past
, line1
, line2
,
222 init(numParams
, isGenerator
);
226 if (m_fullName
!= nullptr && m_maybeIntercepted
!= -1) {
227 unregister_intercept_flag(fullNameRef(), &m_maybeIntercepted
);
235 void Func::destroy(Func
* func
) {
237 if (func
->isClosureBody() || func
->isGeneratorFromClosure()) {
238 Func
** startOfFunc
= (Func
**) mem
;
239 mem
= startOfFunc
- 1; // move back by a pointer
245 Func
* Func::clone() const {
246 Func
* f
= new (allocFuncMem(
249 isClosureBody() || isGeneratorFromClosure()
251 f
->initPrologues(m_numParams
, isGenerator());
252 f
->m_funcId
= InvalidFuncId
;
256 const Func
* Func::cloneAndSetClass(Class
* cls
) const {
257 if (const Func
* ret
= findCachedClone(cls
)) {
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
)) {
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();
275 nextFunc
= &nextFunc
[0]->nextClonedClosure();
277 *nextFunc
= clonedFunc
;
282 const Func
* Func::findCachedClone(Class
* cls
) const {
283 const Func
* nextFunc
= this;
285 if (nextFunc
->cls() == cls
) {
288 nextFunc
= nextFunc
->nextClonedClosure();
293 void Func::rename(const StringData
* name
) {
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
) {
319 const EHEnt
* Func::findEH(Offset o
) const {
320 assert(o
>= base() && o
< past());
321 const EHEnt
* eh
= nullptr;
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
) {
333 const FPIEnt
* Func::findFPI(Offset o
) const {
334 assert(o
>= base() && o
< past());
335 const FPIEnt
* fe
= nullptr;
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
) {
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];
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
) {
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
) {
386 if (isUnique() && RuntimeOption::RepoAuthoritative
) {
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
;
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 {
413 return arg
< m_numParams
|| !(m_attrs
& AttrVariadicByRef
) ||
414 !info() || !(info()->attribute
& ClassInfo::MixedVariableArguments
);
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
;
424 uint64_t* refBits
= &m_refBitVal
;
425 // Grow args, if necessary.
428 shared()->m_refBitPtr
= (uint64_t*)
429 realloc(shared()->m_refBitPtr
, qword
* sizeof(uint64_t));
431 refBits
= shared()->m_refBitPtr
+ qword
- 1;
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) {
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();
467 out
<< preClass()->name()->data() << "::" << m_name
->data();
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
<< ")";
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();
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");
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
;
510 out
<< " to " << it
->m_fault
;
512 if (it
->m_parentIndex
!= -1) {
513 out
<< " parentIndex " << it
->m_parentIndex
;
519 HphpArray
* Func::getStaticLocals() const {
520 return g_vmContext
->getFuncStaticCtx(this);
523 void Func::getFuncInfo(ClassInfo::MethodInfo
* mi
) const {
525 if (info() != nullptr) {
526 // Very large operator=() invocation.
528 // Deep copy the vectors of mi-owned pointers.
529 cloneMembers(mi
->parameters
);
530 cloneMembers(mi
->staticVariables
);
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.
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
;
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();
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
;
566 attr
|= ClassInfo::IsReference
;
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()) {
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());
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
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();
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() {
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 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
;
976 void FuncEmitter::setBuiltinFunc(const ClassInfo::MethodInfo
* info
,
977 BuiltinFunction bif
, BuiltinFunction nif
,
982 m_builtinFuncPtr
= bif
;
983 m_nativeFuncPtr
= nif
;
986 m_docComment
= StringData::GetStaticString(info
->docComment
);
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
;
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
;
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
1050 (m_isGeneratorFromClosure
)
1051 (m_hasGeneratorAsBody
)
1060 (m_retTypeConstraint
)
1061 (m_originalFilename
)
1065 //=============================================================================
1068 FuncRepoProxy::FuncRepoProxy(Repo
& repo
)
1070 #define FRP_OP(c, o) \
1071 , m_##o##Local(repo, RepoIdLocal), m_##o##Central(repo, RepoIdCentral)
1075 #define FRP_OP(c, o) \
1076 m_##o[RepoIdLocal] = &m_##o##Local; \
1077 m_##o[RepoIdCentral] = &m_##o##Central;
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,"
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
,
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);
1120 void FuncRepoProxy::GetFuncsStmt
1121 ::get(UnitEmitter
& ue
) {
1122 RepoTxn
txn(m_repo
);
1124 std::stringstream ssSelect
;
1125 ssSelect
<< "SELECT funcSn,preClassId,name,top,extraData "
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());
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);
1143 if (preClassId
< 0) {
1144 fe
= ue
.newFuncEmitter(name
, top
);
1146 PreClassEmitter
* pce
= ue
.pce(preClassId
);
1147 fe
= ue
.newMethodEmitter(name
, pce
);
1148 bool added UNUSED
= pce
->addMethod(fe
);
1151 assert(fe
->sn() == funcSn
);
1153 fe
->serdeMetaData(extraBlob
);
1154 fe
->finish(fe
->past(), true);
1155 ue
.recordFunction(fe
);
1157 } while (!query
.done());