2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_VM_FUNC_H_
18 #define incl_HPHP_VM_FUNC_H_
20 #include "hphp/runtime/vm/bytecode.h"
21 #include "hphp/runtime/vm/type_constraint.h"
22 #include "hphp/runtime/vm/repo_helpers.h"
23 #include "hphp/runtime/vm/indexed_string_map.h"
24 #include "hphp/runtime/base/intercept.h"
28 static const int kNumFixedPrologues
= 6;
30 typedef TypedValue
*(*BuiltinFunction
)(ActRec
* ar
);
33 * Metadata about a php function or object method.
36 friend class FuncEmitter
;
38 typedef hphp_hash_map
<const StringData
*, TypedValue
, string_data_hash
,
39 string_data_isame
> UserAttributeMap
;
41 struct ParamInfo
{ // Parameter default value info.
42 // construct a dummy ParamInfo
44 : m_builtinType(KindOfInvalid
), m_funcletOff(InvalidAbsoluteOffset
),
45 m_phpCode(nullptr), m_userType(nullptr) {
46 tvWriteUninit(&m_defVal
);
50 void serde(SerDe
& sd
) {
51 const StringData
* tcName
= m_typeConstraint
.typeName();
52 TypeConstraint::Flags tcFlags
= m_typeConstraint
.flags();
64 if (SerDe::deserializing
) {
65 setTypeConstraint(TypeConstraint(tcName
, tcFlags
));
69 void setBuiltinType(DataType type
) { m_builtinType
= type
; }
70 DataType
builtinType() const { return m_builtinType
; }
72 void setFuncletOff(Offset funcletOff
) { m_funcletOff
= funcletOff
; }
73 Offset
funcletOff() const { return m_funcletOff
; }
75 bool hasDefaultValue() const {
76 return m_funcletOff
!= InvalidAbsoluteOffset
;
78 bool hasNonNullDefaultValue() const {
79 return hasDefaultValue() && m_defVal
.m_type
!= KindOfNull
;
81 bool hasScalarDefaultValue() const {
82 return hasDefaultValue() && m_defVal
.m_type
!= KindOfUninit
;
84 void setDefaultValue(const TypedValue
& defVal
) { m_defVal
= defVal
; }
85 const TypedValue
& defaultValue() const { return m_defVal
; }
87 void setPhpCode(const StringData
* phpCode
) { m_phpCode
= phpCode
; }
88 const StringData
* phpCode() const { return m_phpCode
; }
90 void setTypeConstraint(const TypeConstraint
& tc
) { m_typeConstraint
= tc
; }
91 const TypeConstraint
& typeConstraint() const { return m_typeConstraint
; }
93 void addUserAttribute(const StringData
* name
, TypedValue tv
) {
94 m_userAttributes
[name
] = tv
;
96 void setUserAttributes(const Func::UserAttributeMap
& uaMap
) {
97 m_userAttributes
= uaMap
;
99 const Func::UserAttributeMap
& userAttributes() const {
100 return m_userAttributes
;
102 void setUserType(const StringData
* userType
) {
103 m_userType
= userType
;
105 const StringData
* userType() const {
110 DataType m_builtinType
; // typehint for builtins
111 Offset m_funcletOff
; // If no default: InvalidAbsoluteOffset.
112 TypedValue m_defVal
; // Set to uninit null if there is no default value
113 // or if there is a non-scalar default value.
114 const StringData
* m_phpCode
; // eval'able PHP code.
115 TypeConstraint m_typeConstraint
;
117 Func::UserAttributeMap m_userAttributes
;
118 // the type the user typed in source code, contains type parameters and all
119 const StringData
* m_userType
;
121 struct SVInfo
{ // Static variable info.
122 const StringData
* name
;
123 const StringData
* phpCode
; // eval'able PHP or NULL if no default.
125 template<class SerDe
> void serde(SerDe
& sd
) { sd(name
)(phpCode
); }
128 typedef FixedVector
<ParamInfo
> ParamInfoVec
;
129 typedef FixedVector
<SVInfo
> SVInfoVec
;
130 typedef FixedVector
<EHEnt
> EHEntVec
;
131 typedef FixedVector
<FPIEnt
> FPIEntVec
;
133 typedef uint32_t FuncId
;
134 static const FuncId InvalidId
= -1LL;
136 Func(Unit
& unit
, Id id
, int line1
, int line2
, Offset base
,
137 Offset past
, const StringData
* name
, Attr attrs
, bool top
,
138 const StringData
* docComment
, int numParams
, bool isGenerator
);
139 Func(Unit
& unit
, PreClass
* preClass
, int line1
,
140 int line2
, Offset base
, Offset past
,
141 const StringData
* name
, Attr attrs
, bool top
,
142 const StringData
* docComment
, int numParams
, bool isGenerator
);
144 static void destroy(Func
* func
);
147 const Func
* cloneAndSetClass(Class
* cls
) const;
149 void validate() const {
151 assert(this && m_magic
== kMagic
);
155 FuncId
getFuncId() const {
156 assert(m_funcId
!= InvalidId
);
159 void setFuncId(FuncId id
);
162 void rename(const StringData
* name
);
163 int numSlotsInFrame() const {
164 return shared()->m_numLocals
+ shared()->m_numIterators
* kNumIterCells
;
166 Id
lookupVarId(const StringData
* name
) const;
169 * Return true if Offset o is inside the protected region of a fault
170 * funclet for iterId, otherwise false. itRef will be set to true if
171 * the iterator was initialized with MIterInit*, false if the iterator
172 * was initialized with IterInit*.
174 bool checkIterScope(Offset o
, Id iterId
, bool& itRef
) const;
177 * Find the first EHEnt that covers a given offset, or return null.
179 const EHEnt
* findEH(Offset o
) const;
182 * Locate FPI regions by offset.
184 const FPIEnt
* findFPI(Offset o
) const;
185 const FPIEnt
* findPrecedingFPI(Offset o
) const;
187 void parametersCompat(const PreClass
* preClass
, const Func
* imeth
) const;
189 // This can be thought of as "if I look up this Func's name while in fromUnit,
190 // will I always get this Func back?" This is important for the translator: if
191 // this condition holds, it allows for some translation-time optimizations by
192 // making assumptions about where function calls will go.
193 bool isNameBindingImmutable(const Unit
* fromUnit
) const;
195 void setMaxStackCells(int cells
) { m_maxStackCells
= cells
; }
196 int maxStackCells() const {
197 // All functions have to return something, which pushes at least 1 cell
198 assert(m_maxStackCells
> 0);
199 return m_maxStackCells
;
202 bool byRef(int32_t arg
) const;
203 bool mustBeRef(int32_t arg
) const;
204 void prettyPrint(std::ostream
& out
) const;
206 bool isPseudoMain() const { return m_name
->empty(); }
207 bool isBuiltin() const { return (bool)info(); }
208 bool isMethod() const {
209 return !isPseudoMain() && (bool)cls();
211 bool isTraitMethod() const {
212 PreClass
* pcls
= preClass();
213 return pcls
&& (pcls
->attrs() & AttrTrait
);
215 bool isNonClosureMethod() const {
216 return isMethod() && !isClosureBody();
218 bool isPublic() const { return bool(m_attrs
& AttrPublic
); }
219 bool isStatic() const { return bool(m_attrs
& AttrStatic
); }
220 bool isAbstract() const { return bool(m_attrs
& AttrAbstract
); }
221 bool isUnique() const { return bool(m_attrs
& AttrUnique
); }
222 bool isDestructor() const {
223 return !strcmp(m_name
->data(), "__destruct");
225 bool isPersistent() const { return m_attrs
& AttrPersistent
; }
226 static bool isMagicCallMethodName(const StringData
* name
) {
227 return name
->isame(s___call
) || name
->isame(s___callStatic
);
229 bool isMagicCallMethod() const {
230 return m_name
->isame(s___call
);
232 bool isMagicCallStaticMethod() const {
233 return m_name
->isame(s___callStatic
);
235 bool isMagic() const {
236 return isMagicCallMethod() || isMagicCallStaticMethod();
238 static bool isSpecial(const StringData
* methName
) {
239 return strncmp("86", methName
->data(), 2) == 0;
241 bool isNoInjection() const { return bool(m_attrs
& AttrNoInjection
); }
243 HphpArray
* getStaticLocals() const;
244 void getFuncInfo(ClassInfo::MethodInfo
* mi
) const;
246 Unit
* unit() const { return m_unit
; }
247 PreClass
* preClass() const { return shared()->m_preClass
; }
248 Class
* cls() const { return m_cls
; }
249 void setCls(Class
* cls
) {
253 void setClsAndName(Class
* cls
, const StringData
* name
) {
258 Class
* baseCls() const { return m_baseCls
; }
259 void setBaseCls(Class
* baseCls
) { m_baseCls
= baseCls
; }
260 bool hasPrivateAncestor() const { return m_hasPrivateAncestor
; }
261 void setHasPrivateAncestor(bool b
) { m_hasPrivateAncestor
= b
; }
263 assert(preClass() == nullptr);
264 return shared()->m_id
;
266 Offset
base() const { return shared()->m_base
; }
267 const inline Opcode
* getEntry() const {
268 return m_unit
->entry() + shared()->m_base
;
270 Offset
past() const { return shared()->m_past
; }
271 int line1() const { return shared()->m_line1
; }
272 int line2() const { return shared()->m_line2
; }
273 DataType
returnType() const { return shared()->m_returnType
; }
274 const SVInfoVec
& staticVars() const { return shared()->m_staticVars
; }
275 const StringData
* name() const {
276 assert(m_name
!= nullptr);
279 CStrRef
nameRef() const {
280 assert(m_name
!= nullptr);
281 return *(String
*)(&m_name
);
283 const StringData
* fullName() const {
284 if (m_fullName
== nullptr) return m_name
;
287 CStrRef
fullNameRef() const {
288 assert(m_fullName
!= nullptr);
289 return *(String
*)(&m_fullName
);
292 static size_t fullNameOffset() {
293 return offsetof(Func
, m_fullName
);
295 static size_t sharedOffset() {
296 return offsetof(Func
, m_shared
);
298 static size_t sharedBaseOffset() {
299 return offsetof(SharedData
, m_base
);
301 char &maybeIntercepted() const { return m_maybeIntercepted
; }
302 int numParams() const { return m_numParams
; }
303 const ParamInfoVec
& params() const { return shared()->m_params
; }
304 int numLocals() const { return shared()->m_numLocals
; }
306 const StringData
* const* localNames() const {
307 return shared()->m_localNames
.accessList();
309 Id
numNamedLocals() const { return shared()->m_localNames
.size(); }
311 // Returns the name of a local variable, or null if this varid is an
313 const StringData
* localVarName(Id id
) const {
315 return id
< numNamedLocals() ? shared()->m_localNames
[id
] : 0;
318 const StringData
* returnTypeConstraint() const {
319 return shared()->m_retTypeConstraint
;
322 int numIterators() const { return shared()->m_numIterators
; }
323 const EHEntVec
& ehtab() const { return shared()->m_ehtab
; }
324 const FPIEntVec
& fpitab() const { return shared()->m_fpitab
; }
325 Attr
attrs() const { return m_attrs
; }
326 void setAttrs(Attr attrs
) { m_attrs
= attrs
; }
327 bool top() const { return shared()->m_top
; }
328 const StringData
* docComment() const { return shared()->m_docComment
; }
329 bool isClosureBody() const { return shared()->m_isClosureBody
; }
330 bool isClonedClosure() const;
331 bool isGenerator() const { return shared()->m_isGenerator
; }
332 bool isGeneratorFromClosure() const {
333 return shared()->m_isGeneratorFromClosure
;
336 * If this function is a generator then it is implemented as a simple
337 * function that just returns another function. hasGeneratorAsBody() will be
338 * true for the outer functions and isGenerator() is true for the
341 * This isn't a pointer to the function itself because it was too hard to
342 * hook the parts up. If you know more and need it, there probably isn't a
343 * technical reason not to.
345 bool hasGeneratorAsBody() const { return shared()->m_hasGeneratorAsBody
; }
346 const Func
* getGeneratorBody(const StringData
* name
) const;
347 bool hasStaticLocals() const { return !shared()->m_staticVars
.empty(); }
348 int numStaticLocals() const { return shared()->m_staticVars
.size(); }
349 const ClassInfo::MethodInfo
* info() const { return shared()->m_info
; }
350 bool isAllowOverride() const;
351 const BuiltinFunction
& nativeFuncPtr() const {
352 return shared()->m_nativeFuncPtr
;
354 const BuiltinFunction
& builtinFuncPtr() const {
355 return shared()->m_builtinFuncPtr
;
357 const UserAttributeMap
& userAttributes() const {
358 return shared()->m_userAttributes
;
362 * Closure's __invoke()s have an extra pointer used to keep cloned versions
363 * of themselves with different contexts.
365 * const here is the equivalent of "mutable" since this is just a cache
367 Func
*& nextClonedClosure() const {
368 assert(isClosureBody() || isGeneratorFromClosure());
369 return ((Func
**)this)[-1];
372 static void* allocFuncMem(
373 const StringData
* name
, int numParams
, bool needsNextClonedClosure
);
375 void setPrologue(int index
, unsigned char* tca
) {
376 m_prologueTable
[index
] = tca
;
378 void setFuncBody(unsigned char* fb
) {
381 unsigned char* getFuncBody() const {
384 unsigned char* getPrologue(int index
) const {
385 return m_prologueTable
[index
];
387 int numPrologues() const {
388 return getMaxNumPrologues(m_numParams
);
390 static int getMaxNumPrologues(int numParams
) {
391 // maximum number of prologues is numParams+2. The extra 2 are for
392 // the case where the number of actual params equals numParams and
393 // the case where the number of actual params is greater than
395 return numParams
+ 2;
397 void resetPrologues() {
398 // Useful when killing code; forget what we've learned about the contents
399 // of the translation cache.
400 initPrologues(m_numParams
, isGenerator());
403 const NamedEntity
* getNamedEntity() const {
404 assert(!m_shared
->m_preClass
);
405 return m_namedEntity
;
407 Slot
methodSlot() const {
411 void setMethodSlot(Slot s
) {
415 Func
** getCachedAddr();
416 Func
* getCached() { return *getCachedAddr(); }
418 unsigned getCachedOffset() const { return m_cachedOffset
; }
420 public: // Offset accessors for the translator.
421 #define X(f) static size_t f##Off() { return offsetof(Func, m_##f); }
435 typedef IndexedStringMap
<const StringData
*,true,Id
> NamedLocalsMap
;
437 struct SharedData
: public AtomicCountable
{
438 PreClass
* m_preClass
;
446 DataType m_returnType
;
447 const ClassInfo::MethodInfo
* m_info
; // For builtins.
448 uint64_t* m_refBitVec
;
449 BuiltinFunction m_builtinFuncPtr
;
450 BuiltinFunction m_nativeFuncPtr
;
451 ParamInfoVec m_params
; // m_params[i] corresponds to parameter i.
452 NamedLocalsMap m_localNames
; // includes parameter names
453 SVInfoVec m_staticVars
;
456 const StringData
* m_docComment
;
457 bool m_top
: 1; // Defined at top level.
458 bool m_isClosureBody
: 1;
459 bool m_isGenerator
: 1;
460 bool m_isGeneratorFromClosure
: 1;
461 bool m_hasGeneratorAsBody
: 1;
462 UserAttributeMap m_userAttributes
;
463 const StringData
* m_retTypeConstraint
;
464 SharedData(PreClass
* preClass
, Id id
, Offset base
,
465 Offset past
, int line1
, int line2
, bool top
,
466 const StringData
* docComment
);
468 void atomicRelease();
470 typedef AtomicSmartPtr
<SharedData
> SharedDataPtr
;
472 static const int kBitsPerQword
= 64;
473 static const StringData
* s___call
;
474 static const StringData
* s___callStatic
;
475 static const int kMagic
= 0xba5eba11;
479 void init(int numParams
, bool isGenerator
);
480 void initPrologues(int numParams
, bool isGenerator
);
481 void appendParam(bool ref
, const ParamInfo
& info
,
482 std::vector
<ParamInfo
>& pBuilder
);
483 void allocVarId(const StringData
* name
);
484 const SharedData
* shared() const { return m_shared
.get(); }
485 SharedData
* shared() { return m_shared
.get(); }
486 const Func
* findCachedClone(Class
* cls
) const;
490 Class
* m_cls
; // The Class that provided this method implementation
491 Class
* m_baseCls
; // The first Class in the inheritance hierarchy that
492 // declared this method; note that this may be an abstract
493 // class that did not provide an implementation
494 const StringData
* m_name
;
495 const StringData
* m_fullName
;
496 SharedDataPtr m_shared
;
498 const NamedEntity
* m_namedEntity
;
501 uint64_t* m_refBitVec
;
502 public: // used by Unit
503 unsigned m_cachedOffset
;
506 int m_magic
; // For asserts only.
512 bool m_hasPrivateAncestor
: 1; // This flag indicates if any of this
513 // Class's ancestors provide a
514 // "private" implementation for this
516 // TODO(#1114385) intercept should work via invalidation.
517 mutable char m_maybeIntercepted
; // -1, 0, or 1. Accessed atomically.
518 unsigned char* volatile m_funcBody
; // Accessed from assembly.
519 // This must be the last field declared in this structure
520 // and the Func class should not be inherited from.
521 unsigned char* volatile m_prologueTable
[kNumFixedPrologues
];
526 typedef std::vector
<Func::SVInfo
> SVInfoVec
;
527 typedef std::vector
<EHEnt
> EHEntVec
;
528 typedef std::vector
<FPIEnt
> FPIEntVec
;
530 struct ParamInfo
: public Func::ParamInfo
{
531 ParamInfo() : m_ref(false) {}
533 void setRef(bool ref
) { m_ref
= ref
; }
534 bool ref() const { return m_ref
; }
536 template<class SerDe
> void serde(SerDe
& sd
) {
537 Func::ParamInfo
* parent
= this;
543 bool m_ref
; // True if parameter is passed by reference.
545 typedef std::vector
<ParamInfo
> ParamInfoVec
;
547 FuncEmitter(UnitEmitter
& ue
, int sn
, Id id
, const StringData
* n
);
548 FuncEmitter(UnitEmitter
& ue
, int sn
, const StringData
* n
,
549 PreClassEmitter
* pce
);
552 void init(int line1
, int line2
, Offset base
, Attr attrs
, bool top
,
553 const StringData
* docComment
);
554 void finish(Offset past
, bool load
);
556 template<class SerDe
> void serdeMetaData(SerDe
&);
562 void appendParam(const StringData
* name
, const ParamInfo
& info
);
563 void setParamFuncletOff(Id id
, Offset off
) {
564 m_params
[id
].setFuncletOff(off
);
566 void allocVarId(const StringData
* name
);
567 Id
lookupVarId(const StringData
* name
) const;
568 bool hasVar(const StringData
* name
) const;
569 Id
numParams() const { return m_params
.size(); }
571 void setReturnTypeConstraint(const StringData
* retTypeConstraint
) {
572 m_retTypeConstraint
= retTypeConstraint
;
576 void freeIterator(Id id
);
577 void setNumIterators(Id numIterators
);
578 Id
numIterators() const { return m_numIterators
; }
580 Id
allocUnnamedLocal();
581 void freeUnnamedLocal(Id id
);
582 Id
numLocals() const { return m_numLocals
; }
583 void setNumLocals(Id numLocals
);
585 void setMaxStackCells(int cells
) { m_maxStackCells
= cells
; }
586 void addStaticVar(Func::SVInfo svInfo
);
588 UnitEmitter
& ue() const { return m_ue
; }
589 PreClassEmitter
* pce() const { return m_pce
; }
590 void setIds(int sn
, Id id
) {
594 int sn() const { return m_sn
; }
596 assert(m_pce
== nullptr);
599 Offset
base() const { return m_base
; }
600 Offset
past() const { return m_past
; }
601 const StringData
* name() const { return m_name
; }
602 const ParamInfoVec
& params() const { return m_params
; }
603 const EHEntVec
& ehtab() const { return m_ehtab
; }
604 EHEntVec
& ehtab() { return m_ehtab
; }
605 const FPIEntVec
& fpitab() const { return m_fpitab
; }
607 void setAttrs(Attr attrs
) { m_attrs
= attrs
; }
608 Attr
attrs() const { return m_attrs
; }
610 void setTop(bool top
) { m_top
= top
; }
611 bool top() { return m_top
; }
613 bool isPseudoMain() const { return m_name
->empty(); }
615 void setIsClosureBody(bool isClosureBody
) { m_isClosureBody
= isClosureBody
; }
616 bool isClosureBody() const { return m_isClosureBody
; }
618 void setIsGenerator(bool isGenerator
) { m_isGenerator
= isGenerator
; }
619 bool isGenerator() const { return m_isGenerator
; }
621 void setIsGeneratorFromClosure(bool b
) { m_isGeneratorFromClosure
= b
; }
622 bool isGeneratorFromClosure() const { return m_isGeneratorFromClosure
; }
624 void setHasGeneratorAsBody(bool b
) { m_hasGeneratorAsBody
= b
; }
625 bool hasGeneratorAsBody() const { return m_hasGeneratorAsBody
; }
627 void setContainsCalls() { m_containsCalls
= true; }
629 void addUserAttribute(const StringData
* name
, TypedValue tv
);
631 void commit(RepoTxn
& txn
) const;
632 Func
* create(Unit
& unit
, PreClass
* preClass
= nullptr) const;
634 void setBuiltinFunc(const ClassInfo::MethodInfo
* info
,
635 BuiltinFunction bif
, BuiltinFunction nif
, Offset base
);
639 void sortFPITab(bool load
);
642 PreClassEmitter
* m_pce
;
649 const StringData
* m_name
;
651 ParamInfoVec m_params
;
652 Func::NamedLocalsMap::Builder m_localNames
;
654 int m_numUnnamedLocals
;
655 int m_activeUnnamedLocals
;
657 Id m_nextFreeIterator
;
659 SVInfoVec m_staticVars
;
661 const StringData
* m_retTypeConstraint
;
667 DataType m_returnType
;
669 const StringData
* m_docComment
;
670 bool m_isClosureBody
;
672 bool m_isGeneratorFromClosure
;
673 bool m_hasGeneratorAsBody
;
674 bool m_containsCalls
;
676 Func::UserAttributeMap m_userAttributes
;
678 const ClassInfo::MethodInfo
* m_info
;
679 BuiltinFunction m_builtinFuncPtr
;
680 BuiltinFunction m_nativeFuncPtr
;
683 class FuncRepoProxy
: public RepoProxy
{
685 friend class FuncEmitter
;
687 explicit FuncRepoProxy(Repo
& repo
);
689 void createSchema(int repoId
, RepoTxn
& txn
);
691 #define FRP_IOP(o) FRP_OP(Insert##o, insert##o)
692 #define FRP_GOP(o) FRP_OP(Get##o, get##o)
696 class InsertFuncStmt
: public RepoProxy::Stmt
{
698 InsertFuncStmt(Repo
& repo
, int repoId
) : Stmt(repo
, repoId
) {}
699 void insert(const FuncEmitter
& fe
,
700 RepoTxn
& txn
, int64_t unitSn
, int funcSn
, Id preClassId
,
701 const StringData
* name
, bool top
);
703 class GetFuncsStmt
: public RepoProxy::Stmt
{
705 GetFuncsStmt(Repo
& repo
, int repoId
) : Stmt(repo
, repoId
) {}
706 void get(UnitEmitter
& ue
);
708 #define FRP_OP(c, o) \
710 c##Stmt& o(int repoId) { return *m_##o[repoId]; } \
712 c##Stmt m_##o##Local; \
713 c##Stmt m_##o##Central; \
714 c##Stmt* m_##o[RepoIdCount];