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 +----------------------------------------------------------------------+
16 #include "hphp/hhbbc/func-util.h"
18 #include "hphp/hhbbc/misc.h"
19 #include "hphp/hhbbc/representation.h"
20 #include "hphp/hhbbc/wide-func.h"
22 #include "hphp/runtime/vm/func.h"
24 namespace HPHP::HHBBC
{
26 //////////////////////////////////////////////////////////////////////
28 const StaticString
s_reified_generics_var("0ReifiedGenerics");
29 const StaticString
s_coeffects_var("0Coeffects");
31 //////////////////////////////////////////////////////////////////////
33 uint32_t closure_num_use_vars(const php::Func
* f
) {
34 // Properties on the closure object are use vars.
35 return f
->cls
->properties
.size();
38 bool is_volatile_local(const php::Func
* func
, LocalId lid
) {
39 auto const& l
= func
->locals
[lid
];
40 if (!l
.name
) return false;
42 return l
.name
->same(s_reified_generics_var
.get()) ||
43 l
.name
->same(s_coeffects_var
.get()) ||
44 l
.name
->same(s_86metadata
.get()) ||
45 l
.name
->same(s_86productAttributionData
.get());
48 SString
memoize_impl_name(const php::Func
* func
) {
49 always_assert(func
->isMemoizeWrapper
);
50 return Func::genMemoizeImplName(func
->name
);
53 bool check_nargs_in_range(const php::Func
* func
, uint32_t nArgs
) {
54 while (nArgs
< func
->dvEntries
.size()) {
55 if (func
->dvEntries
[nArgs
++] == NoBlockId
) return false;
58 auto& params
= func
->params
;
59 auto size
= params
.size();
61 return size
> 0 && params
[size
- 1].isVariadic
;
66 int dyn_call_error_level(const php::Func
* func
) {
67 auto const def
= [&] {
68 if (!(func
->attrs
& AttrDynamicallyCallable
) ||
69 Cfg::Eval::ForbidDynamicCallsWithAttr
) {
71 if (func
->attrs
& AttrStatic
) {
72 return Cfg::Eval::ForbidDynamicCallsToClsMeth
;
74 return Cfg::Eval::ForbidDynamicCallsToInstMeth
;
76 return Cfg::Eval::ForbidDynamicCallsToFunc
;
81 if (def
> 0 && func
->sampleDynamicCalls
) return 1;
85 bool has_coeffects_local(const php::Func
* func
) {
86 return !func
->coeffectRules
.empty() &&
87 !(func
->coeffectRules
.size() == 1 &&
88 func
->coeffectRules
[0].isGeneratorThis());
91 //////////////////////////////////////////////////////////////////////
95 void copy_into(php::WideFunc
& dst
, const php::WideFunc
& src
) {
96 assertx(!src
.blocks().empty());
97 assertx(!dst
.blocks().empty());
98 always_assert(src
->exnNodes
.empty() || dst
->exnNodes
.empty());
100 auto const delta
= dst
.blocks().size();
101 dst
->exnNodes
.reserve(dst
->exnNodes
.size() + src
->exnNodes
.size());
102 for (auto en
: src
->exnNodes
) {
103 en
.region
.catchEntry
+= delta
;
104 dst
->exnNodes
.push_back(std::move(en
));
106 for (auto src_block
: src
.blocks()) {
107 auto const dst_block
= src_block
.mutate();
108 if (dst_block
->fallthrough
!= NoBlockId
) dst_block
->fallthrough
+= delta
;
109 if (dst_block
->throwExit
!= NoBlockId
) dst_block
->throwExit
+= delta
;
110 for (auto& bc
: dst_block
->hhbcs
) {
111 // When merging functions (used for 86xints) we have to drop the srcLoc,
112 // because it might reference a different unit. Since these functions
113 // are generated, the srcLoc isn't that meaningful anyway.
115 bc
.forEachTarget([&] (BlockId
& b
) { b
+= delta
; });
117 dst
.blocks().push_back(std::move(src_block
));
121 //////////////////////////////////////////////////////////////////////
125 //////////////////////////////////////////////////////////////////////
127 bool append_func(php::Func
* dst
, const php::Func
& src
) {
128 if (src
.numIters
|| src
.locals
.size()) return false;
129 if (src
.exnNodes
.size() && dst
->exnNodes
.size()) return false;
131 auto const src_func
= php::WideFunc::cns(&src
);
132 auto dst_func
= php::WideFunc::mut(dst
);
135 for (auto const bid
: dst_func
.blockRange()) {
136 auto const& cblk
= dst_func
.blocks()[bid
];
137 if (cblk
->hhbcs
.back().op
!= Op::RetC
) continue;
138 auto const blk
= dst_func
.blocks()[bid
].mutate();
139 blk
->hhbcs
.back() = bc::PopC
{};
140 blk
->fallthrough
= dst_func
.blocks().size();
143 if (!ok
) return false;
144 copy_into(dst_func
, src_func
);
148 bool append_86cinit(php::Func
* dst
, const php::Func
& src
) {
149 if (src
.numIters
) return false;
150 if (src
.locals
.size() != 1 || dst
->locals
.size() != 1) return false;
151 if (src
.exnNodes
.size() || dst
->exnNodes
.size()) return false;
153 auto dst_func
= php::WideFunc::mut(dst
);
154 auto const src_func
= php::WideFunc::cns(&src
);
156 auto const& dst_switch_blk
= dst_func
.blocks()[0].mutate();
157 always_assert(dst_switch_blk
->hhbcs
.back().op
== Op::SSwitch
);
158 auto const& src_switch_blk
= src_func
.blocks()[0];
159 always_assert(src_switch_blk
->hhbcs
.back().op
== Op::SSwitch
);
160 auto& dst_cases
= dst_switch_blk
->hhbcs
.back().SSwitch
.targets
;
161 dst_cases
.pop_back();
162 dst_func
.blocks().pop_back();
163 auto const delta
= dst_cases
.size();
164 auto const& src_cases
= src_switch_blk
->hhbcs
.back().SSwitch
.targets
;
166 for (auto const& src_case
: src_cases
) {
167 dst_cases
.push_back(std::make_pair(src_case
.first
, src_case
.second
+ delta
));
168 auto src_block
= src_func
.blocks()[src_case
.second
];
169 auto dst_block
= src_block
.mutate();
170 for (auto& bc
: dst_block
->hhbcs
) {
171 // When merging functions (used for 86xints) we have to drop the srcLoc,
172 // because it might reference a different unit. Since these functions
173 // are generated, the srcLoc isn't that meaningful anyway.
176 dst_func
.blocks().push_back(std::move(src_block
));
182 BlockId
make_block(php::WideFunc
& func
, const php::Block
* srcBlk
) {
183 auto newBlk
= copy_ptr
<php::Block
>{php::Block
{}};
184 auto const blk
= newBlk
.mutate();
185 blk
->exnNodeId
= srcBlk
->exnNodeId
;
186 blk
->throwExit
= srcBlk
->throwExit
;
187 auto const bid
= func
.blocks().size();
188 func
.blocks().push_back(std::move(newBlk
));
192 php::FuncBase::FuncBase(const FuncBase
& other
) {
193 always_assert(!other
.isNative
);
194 // If we don't copy this over, we end up with garbage in `isNative`
195 isNative
= other
.isNative
;
196 exnNodes
= other
.exnNodes
;
197 rawBlocks
= other
.rawBlocks
;
200 //////////////////////////////////////////////////////////////////////
202 std::string
func_fullname(const php::Func
& f
) {
203 if (!f
.cls
) return f
.name
->toCppString();
204 return folly::sformat("{}::{}", f
.cls
->name
, f
.name
);
207 //////////////////////////////////////////////////////////////////////
209 bool is_86init_func(const php::Func
& f
) {
211 f
.name
== s_86cinit
.get() ||
212 f
.name
== s_86pinit
.get() ||
213 f
.name
== s_86sinit
.get() ||
214 f
.name
== s_86linit
.get();
217 //////////////////////////////////////////////////////////////////////