Codemod asserts to assertxs in the runtime
[hiphop-php.git] / hphp / runtime / base / intercept.cpp
bloba7dd40b1b5c038efe432f589ecce967e55bffe7d
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
18 #include <vector>
19 #include <utility>
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 ///////////////////////////////////////////////////////////////////////////////
40 namespace HPHP {
42 TRACE_SET_MOD(intercept);
44 struct InterceptRequestData final : RequestEventHandler {
45 InterceptRequestData() {}
47 void clear() {
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;
60 bool empty() const {
61 return !m_intercept_handlers.hasValue() ||
62 m_intercept_handlers->empty();
64 void clearHandlers() {
65 m_intercept_handlers.clear();
68 private:
69 Variant m_global_handler;
70 req::Optional<req::StringIMap<Variant>> m_intercept_handlers;
72 IMPLEMENT_STATIC_REQUEST_LOCAL(InterceptRequestData, s_intercept_data);
74 static Mutex s_mutex;
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
80 * with this name.
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) {
90 *flag = 1;
94 bool register_intercept(const String& name, const Variant& callback,
95 const Variant& data) {
96 if (!callback.toBoolean()) {
97 if (name.empty()) {
98 s_intercept_data->global_handler().unset();
99 s_intercept_data->clear();
100 } else {
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;
106 handlers.erase(it);
110 return true;
113 EventHook::EnableIntercept();
115 Array handler = make_packed_array(callback, data);
117 if (name.empty()) {
118 s_intercept_data->global_handler() = handler;
119 s_intercept_data->clearHandlers();
120 } else {
121 auto& handlers = s_intercept_data->intercept_handlers();
122 handlers[name] = handler;
125 Lock lock(s_mutex);
126 if (name.empty()) {
127 for (auto& entry : s_registered_flags) {
128 flag_maybe_intercepted(entry.second.second);
130 } else {
131 StringData* sd = name.get();
132 if (!sd->isStatic()) {
133 sd = makeStaticString(sd);
135 auto &entry = s_registered_flags[StrNR(sd)];
136 entry.first = true;
137 flag_maybe_intercepted(entry.second);
140 return true;
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()) {
153 return nullptr;
155 return handler;
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);
161 if (*flag == -1) {
162 Lock lock(s_mutex);
163 if (*flag == -1) {
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);
170 *flag = entry.first;
172 if (!*flag) return nullptr;
175 Variant *handler = get_enabled_intercept_handler(name);
176 if (handler == nullptr) {
177 return nullptr;
179 assertx(*flag);
180 return handler;
183 void unregister_intercept_flag(const String& name, int8_t *flag) {
184 Lock lock(s_mutex);
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);
192 break;
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);
208 if (!func) {
209 // It's the caller's responsibility to ensure that the old function
210 // exists.
211 not_reached();
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
220 // functions.
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 ///////////////////////////////////////////////////////////////////////////////