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/runtime/base/intercept.h"
21 #include "hphp/runtime/base/request-local.h"
22 #include "hphp/runtime/base/request-event-handler.h"
23 #include "hphp/runtime/base/array-init.h"
24 #include "hphp/runtime/base/array-iterator.h"
25 #include "hphp/runtime/base/builtin-functions.h"
26 #include "hphp/runtime/vm/jit/target-cache.h"
27 #include "hphp/runtime/vm/unit.h"
28 #include "hphp/runtime/vm/event-hook.h"
30 #include "hphp/parser/parser.h"
31 #include "hphp/util/lock.h"
33 #include "hphp/runtime/base/unit-cache.h"
34 #include "hphp/util/trace.h"
36 using namespace HPHP::Trace
;
38 ///////////////////////////////////////////////////////////////////////////////
42 TRACE_SET_MOD(intercept
);
44 struct InterceptRequestData final
: RequestEventHandler
{
45 InterceptRequestData() {}
48 m_global_handler
.releaseForSweep();
49 m_intercept_handlers
.clear();
52 void requestInit() override
{ clear(); }
53 void requestShutdown() override
{ clear(); }
55 Variant
& global_handler() { return m_global_handler
; }
56 req::StringIMap
<Variant
>& intercept_handlers() {
57 if (!m_intercept_handlers
) m_intercept_handlers
.emplace();
58 return *m_intercept_handlers
;
61 return !m_intercept_handlers
.hasValue() ||
62 m_intercept_handlers
->empty();
64 void clearHandlers() {
65 m_intercept_handlers
.clear();
69 Variant m_global_handler
;
70 req::Optional
<req::StringIMap
<Variant
>> m_intercept_handlers
;
72 IMPLEMENT_STATIC_REQUEST_LOCAL(InterceptRequestData
, s_intercept_data
);
77 * The bool indicates whether fb_intercept has ever been called
78 * on a function with this name.
79 * The vector contains a list of maybeIntercepted flags for functions
82 typedef StringIMap
<std::pair
<bool,std::vector
<int8_t*>>> RegisteredFlagsMap
;
84 static RegisteredFlagsMap s_registered_flags
;
86 ///////////////////////////////////////////////////////////////////////////////
88 static void flag_maybe_intercepted(std::vector
<int8_t*> &flags
) {
89 for (auto flag
: flags
) {
94 bool register_intercept(const String
& name
, const Variant
& callback
,
95 const Variant
& data
) {
96 if (!callback
.toBoolean()) {
98 s_intercept_data
->global_handler().unset();
99 s_intercept_data
->clear();
101 if (!s_intercept_data
->empty()) {
102 auto& handlers
= s_intercept_data
->intercept_handlers();
103 auto it
= handlers
.find(name
);
104 if (it
!= handlers
.end()) {
105 auto tmp
= it
->second
;
113 EventHook::EnableIntercept();
115 Array handler
= make_packed_array(callback
, data
);
118 s_intercept_data
->global_handler() = handler
;
119 s_intercept_data
->clearHandlers();
121 auto& handlers
= s_intercept_data
->intercept_handlers();
122 handlers
[name
] = handler
;
127 for (auto& entry
: s_registered_flags
) {
128 flag_maybe_intercepted(entry
.second
.second
);
131 StringData
* sd
= name
.get();
132 if (!sd
->isStatic()) {
133 sd
= makeStaticString(sd
);
135 auto &entry
= s_registered_flags
[StrNR(sd
)];
137 flag_maybe_intercepted(entry
.second
);
143 static Variant
*get_enabled_intercept_handler(const String
& name
) {
144 if (!s_intercept_data
->empty()) {
145 auto& handlers
= s_intercept_data
->intercept_handlers();
146 auto iter
= handlers
.find(name
);
147 if (iter
!= handlers
.end()) {
148 return &iter
->second
;
151 auto handler
= &s_intercept_data
->global_handler();
152 if (handler
->isNull()) {
158 Variant
*get_intercept_handler(const String
& name
, int8_t* flag
) {
159 TRACE(1, "get_intercept_handler %s flag is %d\n",
160 name
.get()->data(), (int)*flag
);
164 StringData
*sd
= name
.get();
165 if (!sd
->isStatic()) {
166 sd
= makeStaticString(sd
);
168 auto &entry
= s_registered_flags
[StrNR(sd
)];
169 entry
.second
.push_back(flag
);
172 if (!*flag
) return nullptr;
175 Variant
*handler
= get_enabled_intercept_handler(name
);
176 if (handler
== nullptr) {
183 void unregister_intercept_flag(const String
& name
, int8_t *flag
) {
185 RegisteredFlagsMap::iterator iter
=
186 s_registered_flags
.find(name
);
187 if (iter
!= s_registered_flags
.end()) {
188 std::vector
<int8_t*> &flags
= iter
->second
.second
;
189 for (int i
= flags
.size(); i
--; ) {
190 if (flag
== flags
[i
]) {
191 flags
.erase(flags
.begin() + i
);
198 ///////////////////////////////////////////////////////////////////////////////
199 // fb_rename_function()
201 void rename_function(const String
& old_name
, const String
& new_name
) {
202 auto const old
= old_name
.get();
203 auto const n3w
= new_name
.get();
204 auto const oldNe
= const_cast<NamedEntity
*>(NamedEntity::get(old
));
205 auto const newNe
= const_cast<NamedEntity
*>(NamedEntity::get(n3w
));
207 Func
* func
= Unit::lookupFunc(oldNe
);
209 // It's the caller's responsibility to ensure that the old function
214 // Interceptable functions can be renamed even when
215 // JitEnableRenameFunction is false.
216 if (!(func
->attrs() & AttrInterceptable
)) {
217 if (!RuntimeOption::EvalJitEnableRenameFunction
) {
218 // When EvalJitEnableRenameFunction is false, the translator may
219 // wire non-AttrInterceptable Func*'s into the TC. Don't rename
221 raise_error("fb_rename_function must be explicitly enabled"
222 "(-v Eval.JitEnableRenameFunction=true)");
226 auto const fnew
= Unit::lookupFunc(newNe
);
227 if (fnew
&& fnew
!= func
) {
228 raise_error("Function already defined: %s", n3w
->data());
231 always_assert(!rds::isPersistentHandle(oldNe
->getFuncHandle()));
232 oldNe
->setCachedFunc(nullptr);
233 newNe
->m_cachedFunc
.bind();
234 newNe
->setCachedFunc(func
);
236 if (RuntimeOption::EvalJit
) {
237 jit::invalidateForRenameFunction(old
);
241 ///////////////////////////////////////////////////////////////////////////////