2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 #include "hphp/runtime/vm/func-emitter.h"
19 #include "hphp/runtime/ext/extension.h"
20 #include "hphp/runtime/base/unit-cache.h"
21 #include "hphp/runtime/base/rds.h"
23 #include "hphp/runtime/vm/blob-helper.h"
24 #include "hphp/runtime/vm/bytecode.h"
25 #include "hphp/runtime/vm/native.h"
26 #include "hphp/runtime/vm/reified-generics.h"
27 #include "hphp/runtime/vm/repo.h"
28 #include "hphp/runtime/vm/runtime.h"
30 #include "hphp/runtime/vm/jit/types.h"
32 #include "hphp/runtime/vm/verifier/cfg.h"
34 #include "hphp/system/systemlib.h"
36 #include "hphp/util/atomic-vector.h"
37 #include "hphp/util/file.h"
38 #include "hphp/util/trace.h"
41 ///////////////////////////////////////////////////////////////////////////////
44 FuncEmitter::FuncEmitter(UnitEmitter
& ue
, int sn
, Id id
, const StringData
* n
)
52 , hniReturnType(folly::none
)
53 , retUserType(nullptr)
55 , originalFilename(nullptr)
56 , memoizePropName(nullptr)
57 , memoizeGuardPropName(nullptr)
58 , memoizeSharedPropIndex(0)
60 , m_numUnnamedLocals(0)
61 , m_activeUnnamedLocals(0)
63 , m_nextFreeIterator(0)
64 , m_ehTabSorted(false)
67 FuncEmitter::FuncEmitter(UnitEmitter
& ue
, int sn
, const StringData
* n
,
76 , hniReturnType(folly::none
)
77 , retUserType(nullptr)
79 , originalFilename(nullptr)
80 , memoizePropName(nullptr)
81 , memoizeGuardPropName(nullptr)
82 , memoizeSharedPropIndex(0)
84 , m_numUnnamedLocals(0)
85 , m_activeUnnamedLocals(0)
87 , m_nextFreeIterator(0)
88 , m_ehTabSorted(false)
91 FuncEmitter::~FuncEmitter() {
95 ///////////////////////////////////////////////////////////////////////////////
96 // Initialization and execution.
98 void FuncEmitter::init(int l1
, int l2
, Offset base_
, Attr attrs_
, bool top_
,
99 const StringData
* docComment_
) {
104 attrs
= fix_attrs(attrs_
);
105 docComment
= docComment_
;
107 if (!isPseudoMain()) {
108 if (!SystemLib::s_inited
) {
109 assertx(attrs
& AttrBuiltin
);
114 void FuncEmitter::finish(Offset past_
) {
119 void FuncEmitter::commit(RepoTxn
& txn
) const {
120 Repo
& repo
= Repo::get();
121 FuncRepoProxy
& frp
= repo
.frp();
122 int repoId
= m_ue
.m_repoId
;
123 int64_t usn
= m_ue
.m_sn
;
125 frp
.insertFunc
[repoId
]
126 .insert(*this, txn
, usn
, m_sn
, m_pce
? m_pce
->id() : -1, name
, top
);
129 Func
* FuncEmitter::create(Unit
& unit
, PreClass
* preClass
/* = NULL */) const {
130 bool isGenerated
= isdigit(name
->data()[0]);
132 auto attrs
= fix_attrs(this->attrs
);
133 if (preClass
&& preClass
->attrs() & AttrInterface
) {
134 attrs
|= AttrAbstract
;
136 if (attrs
& AttrIsMethCaller
&& RuntimeOption::RepoAuthoritative
) {
137 attrs
|= AttrPersistent
| AttrUnique
;
139 if (attrs
& AttrPersistent
&& !preClass
) {
140 if ((RuntimeOption::EvalJitEnableRenameFunction
||
141 attrs
& AttrInterceptable
||
142 (!RuntimeOption::RepoAuthoritative
&& SystemLib::s_inited
))) {
143 if (attrs
& AttrBuiltin
) {
144 SystemLib::s_anyNonPersistentBuiltins
= true;
146 attrs
= Attr(attrs
& ~AttrPersistent
);
149 assertx(preClass
|| !(attrs
& AttrBuiltin
) || (attrs
& AttrIsMethCaller
));
151 if (!RuntimeOption::RepoAuthoritative
) {
152 // In non-RepoAuthoritative mode, any function could get a VarEnv because
153 // of evalPHPDebugger.
154 attrs
|= AttrMayUseVV
;
155 } else if ((attrs
& AttrInterceptable
) &&
157 !Func::isSpecial(name
) &&
159 // intercepted functions need to pass all args through
160 // to the interceptee
161 attrs
|= AttrMayUseVV
;
164 attrs
|= AttrVariadicParam
;
166 if (isAsync
&& !isGenerator
) {
167 // Async functions can return results directly.
168 attrs
|= AttrSupportsAsyncEagerReturn
;
171 assertx(!m_pce
== !preClass
);
172 auto f
= m_ue
.newFunc(this, unit
, name
, attrs
, params
.size());
174 f
->m_isPreFunc
= !!preClass
;
176 auto const uait
= userAttributes
.find(s___Reified
.get());
177 auto const hasReifiedGenerics
= uait
!= userAttributes
.end();
179 bool const needsExtendedSharedData
=
181 line2
- line1
>= Func::kSmallDeltaLimit
||
182 past
- base
>= Func::kSmallDeltaLimit
||
186 needsExtendedSharedData
187 ? new Func::ExtendedSharedData(preClass
, base
, past
, line1
, line2
,
188 top
, !containsCalls
, docComment
)
189 : new Func::SharedData(preClass
, base
, past
,
190 line1
, line2
, top
, !containsCalls
, docComment
)
193 f
->init(params
.size());
195 if (auto const ex
= f
->extShared()) {
196 ex
->m_hasExtendedSharedData
= true;
197 ex
->m_arFuncPtr
= nullptr;
198 ex
->m_nativeFuncPtr
= nullptr;
201 ex
->m_returnByValue
= false;
202 ex
->m_isMemoizeWrapper
= false;
203 ex
->m_isMemoizeWrapperLSB
= false;
206 std::vector
<Func::ParamInfo
> fParams
;
207 for (unsigned i
= 0; i
< params
.size(); ++i
) {
208 Func::ParamInfo pi
= params
[i
];
209 if (pi
.isVariadic()) {
210 pi
.builtinType
= RuntimeOption::EvalHackArrDVArrs
211 ? KindOfVec
: KindOfArray
;
213 f
->appendParam(params
[i
].inout
, pi
, fParams
);
216 auto const originalFullName
=
217 (!originalFilename
||
218 !RuntimeOption::RepoAuthoritative
||
219 FileUtil::isAbsolutePath(originalFilename
->slice())) ?
221 makeStaticString(RuntimeOption::SourceRoot
+
222 originalFilename
->toCppString());
224 f
->shared()->m_localNames
.create(m_localNames
);
225 f
->shared()->m_numLocals
= m_numLocals
;
226 f
->shared()->m_numIterators
= m_numIterators
;
227 f
->m_maxStackCells
= maxStackCells
;
228 f
->shared()->m_ehtab
= ehtab
;
229 f
->shared()->m_isClosureBody
= isClosureBody
;
230 f
->shared()->m_isAsync
= isAsync
;
231 f
->shared()->m_isGenerator
= isGenerator
;
232 f
->shared()->m_isPairGenerator
= isPairGenerator
;
233 f
->shared()->m_userAttributes
= userAttributes
;
234 f
->shared()->m_retTypeConstraint
= retTypeConstraint
;
235 f
->shared()->m_retUserType
= retUserType
;
236 f
->shared()->m_originalFilename
= originalFullName
;
237 f
->shared()->m_isGenerated
= isGenerated
;
238 f
->shared()->m_repoReturnType
= repoReturnType
;
239 f
->shared()->m_repoAwaitedReturnType
= repoAwaitedReturnType
;
240 f
->shared()->m_isMemoizeWrapper
= isMemoizeWrapper
;
241 f
->shared()->m_isMemoizeWrapperLSB
= isMemoizeWrapperLSB
;
242 f
->shared()->m_hasReifiedGenerics
= hasReifiedGenerics
;
243 f
->shared()->m_isRxDisabled
= isRxDisabled
;
245 if (hasReifiedGenerics
) {
246 auto tv
= uait
->second
;
247 assertx(tvIsVecOrVArray(tv
));
248 f
->extShared()->m_reifiedGenericsInfo
=
249 extractSizeAndPosFromReifiedAttribute(tv
.m_data
.parr
);
253 auto const ex
= f
->extShared();
255 ex
->m_hniReturnType
= hniReturnType
;
257 auto const info
= getNativeInfo();
259 Attr dummy
= AttrNone
;
260 auto nativeAttributes
= parseNativeAttributes(dummy
);
261 Native::getFunctionPointers(
268 if (ex
->m_nativeFuncPtr
) {
269 if (info
.sig
.ret
== Native::NativeSig::Type::MixedTV
) {
270 ex
->m_returnByValue
= true;
272 int extra
= isMethod() ? 1 : 0;
273 assertx(info
.sig
.args
.size() == params
.size() + extra
);
274 for (auto i
= params
.size(); i
--; ) {
275 switch (info
.sig
.args
[extra
+ i
]) {
276 case Native::NativeSig::Type::ObjectArg
:
277 case Native::NativeSig::Type::StringArg
:
278 case Native::NativeSig::Type::ArrayArg
:
279 case Native::NativeSig::Type::ResourceArg
:
280 case Native::NativeSig::Type::MixedTV
:
281 fParams
[i
].nativeArg
= true;
290 f
->finishedEmittingParams(fParams
);
294 String
FuncEmitter::nativeFullname() const {
295 return Native::fullName(name
, m_pce
? m_pce
->name() : nullptr,
296 (attrs
& AttrStatic
));
299 Native::NativeFunctionInfo
FuncEmitter::getNativeInfo() const {
300 return Native::getNativeFunction(
303 m_pce
? m_pce
->name() : nullptr,
308 ///////////////////////////////////////////////////////////////////////////////
309 // Locals, iterators, and parameters.
311 void FuncEmitter::allocVarId(const StringData
* name
) {
312 assertx(name
!= nullptr);
313 // Unnamed locals are segregated (they all come after the named locals).
314 assertx(m_numUnnamedLocals
== 0);
316 if (m_localNames
.find(name
) == m_localNames
.end()) {
317 id
= (m_numLocals
++);
318 assertx(id
== (int)m_localNames
.size());
319 m_localNames
.add(name
, name
);
323 Id
FuncEmitter::allocIterator() {
324 assertx(m_numIterators
>= m_nextFreeIterator
);
325 Id id
= m_nextFreeIterator
++;
326 if (m_numIterators
< m_nextFreeIterator
) {
327 m_numIterators
= m_nextFreeIterator
;
332 Id
FuncEmitter::allocUnnamedLocal() {
333 ++m_activeUnnamedLocals
;
334 if (m_activeUnnamedLocals
> m_numUnnamedLocals
) {
336 ++m_numUnnamedLocals
;
338 return numNamedLocals() - 1 + m_activeUnnamedLocals
;
342 ///////////////////////////////////////////////////////////////////////////////
345 EHEnt
& FuncEmitter::addEHEnt() {
346 assertx(!m_ehTabSorted
347 || "should only mark the ehtab as sorted after adding all of them");
348 ehtab
.emplace_back();
349 ehtab
.back().m_parentIndex
= 7777;
356 * Ordering on EHEnts where e1 < e2 iff
358 * a) e1 and e2 do not overlap, and e1 comes first
362 bool operator()(const EHEnt
& e1
, const EHEnt
& e2
) const {
363 if (e1
.m_base
== e2
.m_base
) {
364 return e1
.m_past
> e2
.m_past
;
366 return e1
.m_base
< e2
.m_base
;
372 void FuncEmitter::sortEHTab() {
373 if (m_ehTabSorted
) return;
375 std::sort(ehtab
.begin(), ehtab
.end(), EHEntComp());
377 for (unsigned int i
= 0; i
< ehtab
.size(); i
++) {
378 ehtab
[i
].m_parentIndex
= -1;
379 for (int j
= i
- 1; j
>= 0; j
--) {
380 if (ehtab
[j
].m_past
>= ehtab
[i
].m_past
) {
381 // parent EHEnt better enclose this one.
382 assertx(ehtab
[j
].m_base
<= ehtab
[i
].m_base
);
383 ehtab
[i
].m_parentIndex
= j
;
392 void FuncEmitter::setEHTabIsSorted() {
393 m_ehTabSorted
= true;
397 for (size_t i
= 0; i
< ehtab
.size(); ++i
) {
400 // Base offsets must be monotonically increasing.
401 always_assert(curBase
<= eh
.m_base
);
404 // Parent should come before, and must enclose this guy.
405 always_assert(eh
.m_parentIndex
== -1 || eh
.m_parentIndex
< i
);
406 if (eh
.m_parentIndex
!= -1) {
407 auto& parent
= ehtab
[eh
.m_parentIndex
];
408 always_assert(parent
.m_base
<= eh
.m_base
&&
409 parent
.m_past
>= eh
.m_past
);
415 ///////////////////////////////////////////////////////////////////////////////
418 /* <<__Native>> user attribute causes systemlib declarations
419 * to hook internal (C++) implementation of funcs/methods
421 * The Native attribute may have the following sub-options
422 * "NoFCallBuiltin": Prevent FCallBuiltin optimization
423 * Effectively forces functions to generate an ActRec
424 * "NoInjection": Do not include this frame in backtraces
426 * e.g. <<__Native("NoFCallBuiltin")>> function foo():mixed;
428 static const StaticString
429 s_native("__Native"),
430 s_nofcallbuiltin("NoFCallBuiltin"),
431 s_noinjection("NoInjection"),
432 s_opcodeimpl("OpCodeImpl");
434 int FuncEmitter::parseNativeAttributes(Attr
& attrs_
) const {
435 int ret
= Native::AttrNone
;
437 auto it
= userAttributes
.find(s_native
.get());
438 assertx(it
!= userAttributes
.end());
439 const TypedValue userAttr
= it
->second
;
440 assertx(isArrayLikeType(userAttr
.m_type
));
441 for (ArrayIter
it(userAttr
.m_data
.parr
); it
; ++it
) {
442 Variant userAttrVal
= it
.second();
443 if (userAttrVal
.isString()) {
444 String userAttrStrVal
= userAttrVal
.toString();
445 if (userAttrStrVal
.get()->isame(s_nofcallbuiltin
.get())) {
446 attrs_
|= AttrNoFCallBuiltin
;
447 } else if (userAttrStrVal
.get()->isame(s_noinjection
.get())) {
448 attrs_
|= AttrNoInjection
;
449 } else if (userAttrStrVal
.get()->isame(s_opcodeimpl
.get())) {
450 ret
|= Native::AttrOpCodeImpl
;
457 Attr
FuncEmitter::fix_attrs(Attr a
) const {
458 if (RuntimeOption::RepoAuthoritative
) return a
;
460 a
= Attr(a
& ~AttrInterceptable
);
462 if (RuntimeOption::EvalJitEnableRenameFunction
) {
463 return a
| AttrInterceptable
;
466 if (!RuntimeOption::DynamicInvokeFunctions
.empty()) {
467 auto const fullName
= [&] {
468 if (m_pce
) return folly::sformat("{}::{}", m_pce
->name(), name
);
469 return name
->toCppString();
471 if (RuntimeOption::DynamicInvokeFunctions
.count(fullName
)) {
472 return a
| AttrInterceptable
;
478 ///////////////////////////////////////////////////////////////////////////////
479 // Serialization/Deserialization
481 template<class SerDe
>
482 void FuncEmitter::serdeMetaData(SerDe
& sd
) {
483 // NOTE: name, top, and a few other fields currently handled outside of this.
487 if (!SerDe::deserializing
) {
488 past_delta
= past
- base
;
489 a
= fix_attrs(attrs
);
499 (repoAwaitedReturnType
)
507 (m_localNames
, [](auto s
) { return s
; })
509 [&](const EHEnt
& prev
, EHEnt cur
) -> EHEnt
{
510 if (!SerDe::deserializing
) {
511 cur
.m_handler
-= cur
.m_past
;
512 cur
.m_past
-= cur
.m_base
;
513 cur
.m_base
-= prev
.m_base
;
515 cur
.m_base
+= prev
.m_base
;
516 cur
.m_past
+= cur
.m_base
;
517 cur
.m_handler
+= cur
.m_past
;
528 if (SerDe::deserializing
) {
529 repoReturnType
.resolveArray(ue());
530 repoAwaitedReturnType
.resolveArray(ue());
531 past
= base
+ past_delta
;
532 attrs
= fix_attrs(a
);
536 ///////////////////////////////////////////////////////////////////////////////
539 FuncRepoProxy::FuncRepoProxy(Repo
& repo
)
541 insertFunc
{InsertFuncStmt(repo
, 0), InsertFuncStmt(repo
, 1)},
542 getFuncs
{GetFuncsStmt(repo
, 0), GetFuncsStmt(repo
, 1)}
545 FuncRepoProxy::~FuncRepoProxy() {
548 void FuncRepoProxy::createSchema(int repoId
, RepoTxn
& txn
) {
549 auto createQuery
= folly::sformat(
551 "(unitSn INTEGER, funcSn INTEGER, preClassId INTEGER, name TEXT, "
552 " top INTEGER, extraData BLOB, PRIMARY KEY (unitSn, funcSn));",
553 m_repo
.table(repoId
, "Func"));
554 txn
.exec(createQuery
);
557 void FuncRepoProxy::InsertFuncStmt
558 ::insert(const FuncEmitter
& fe
,
559 RepoTxn
& txn
, int64_t unitSn
, int funcSn
,
560 Id preClassId
, const StringData
* name
,
563 auto insertQuery
= folly::sformat(
565 "VALUES(@unitSn, @funcSn, @preClassId, @name, @top, @extraData);",
566 m_repo
.table(m_repoId
, "Func"));
567 txn
.prepare(*this, insertQuery
);
570 BlobEncoder extraBlob
{fe
.useGlobalIds()};
571 RepoTxnQuery
query(txn
, *this);
572 query
.bindInt64("@unitSn", unitSn
);
573 query
.bindInt("@funcSn", funcSn
);
574 query
.bindId("@preClassId", preClassId
);
575 query
.bindStaticString("@name", name
);
576 query
.bindBool("@top", top
);
577 const_cast<FuncEmitter
&>(fe
).serdeMetaData(extraBlob
);
578 query
.bindBlob("@extraData", extraBlob
, /* static */ true);
582 void FuncRepoProxy::GetFuncsStmt
583 ::get(UnitEmitter
& ue
) {
584 auto txn
= RepoTxn
{m_repo
.begin()};
586 auto selectQuery
= folly::sformat(
587 "SELECT funcSn, preClassId, name, top, extraData "
589 "WHERE unitSn == @unitSn ORDER BY funcSn ASC;",
590 m_repo
.table(m_repoId
, "Func"));
591 txn
.prepare(*this, selectQuery
);
593 RepoTxnQuery
query(txn
, *this);
594 query
.bindInt64("@unitSn", ue
.m_sn
);
598 int funcSn
; /**/ query
.getInt(0, funcSn
);
599 Id preClassId
; /**/ query
.getId(1, preClassId
);
600 StringData
* name
; /**/ query
.getStaticString(2, name
);
601 bool top
; /**/ query
.getBool(3, top
);
602 BlobDecoder extraBlob
= /**/ query
.getBlob(4, ue
.useGlobalIds());
605 if (preClassId
< 0) {
606 fe
= ue
.newFuncEmitter(name
);
608 PreClassEmitter
* pce
= ue
.pce(preClassId
);
609 fe
= ue
.newMethodEmitter(name
, pce
);
610 bool added UNUSED
= pce
->addMethod(fe
);
613 assertx(fe
->sn() == funcSn
);
615 fe
->serdeMetaData(extraBlob
);
616 if (!SystemLib::s_inited
&& !fe
->isPseudoMain()) {
617 assertx(fe
->attrs
& AttrBuiltin
);
618 if (preClassId
< 0) {
619 assertx(fe
->attrs
& AttrPersistent
);
620 assertx(fe
->attrs
& AttrUnique
);
623 fe
->setEHTabIsSorted();
624 fe
->finish(fe
->past
);
626 } while (!query
.done());
630 ///////////////////////////////////////////////////////////////////////////////