Codemod asserts to assertxs in the runtime
[hiphop-php.git] / hphp / runtime / base / runtime-error.cpp
blob8e56645b0630a72a344dd629b8465988dde0282b
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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/base/runtime-error.h"
18 #include "hphp/runtime/base/builtin-functions.h"
19 #include "hphp/runtime/base/execution-context.h"
20 #include "hphp/runtime/base/runtime-option.h"
21 #include "hphp/runtime/base/thread-info.h"
22 #include "hphp/runtime/vm/repo.h"
23 #include "hphp/runtime/vm/repo-global-data.h"
24 #include "hphp/runtime/vm/vm-regs.h"
25 #include "hphp/util/logger.h"
26 #include "hphp/util/string-vsnprintf.h"
28 #ifdef ERROR
29 # undef ERROR
30 #endif
31 #ifdef STRICT
32 # undef STRICT
33 #endif
35 namespace HPHP {
36 ///////////////////////////////////////////////////////////////////////////////
39 * Careful in these functions: they can be called when tl_regState is
40 * DIRTY. ExecutionContext::handleError is dirty-reg safe, but
41 * evaluate other functions that you might need here.
44 void raise_error(const std::string &msg) {
45 g_context->handleError(msg, static_cast<int>(ErrorMode::ERROR), false,
46 ExecutionContext::ErrorThrowMode::Always,
47 "\nFatal error: ",
48 false);
49 always_assert(0);
52 void raise_error(const char *fmt, ...) {
53 std::string msg;
54 va_list ap;
55 va_start(ap, fmt);
56 string_vsnprintf(msg, fmt, ap);
57 va_end(ap);
58 raise_error(msg);
61 void raise_error_without_first_frame(const std::string &msg) {
62 g_context->handleError(msg, static_cast<int>(ErrorMode::ERROR), false,
63 ExecutionContext::ErrorThrowMode::Always,
64 "\nFatal error: ",
65 true);
66 always_assert(0);
69 void raise_recoverable_error(const std::string &msg) {
70 g_context->handleError(
71 msg, static_cast<int>(ErrorMode::RECOVERABLE_ERROR), true,
72 ExecutionContext::ErrorThrowMode::IfUnhandled,
73 "\nCatchable Fatal error: ",
74 false);
77 void raise_recoverable_error_without_first_frame(const std::string &msg) {
78 g_context->handleError(
79 msg, static_cast<int>(ErrorMode::RECOVERABLE_ERROR), true,
80 ExecutionContext::ErrorThrowMode::IfUnhandled,
81 "\nCatchable Fatal error: ",
82 true);
85 void raise_typehint_error(const std::string& msg) {
86 if (RuntimeOption::PHP7_EngineExceptions) {
87 VMRegAnchor _;
88 SystemLib::throwTypeErrorObject(msg);
90 raise_recoverable_error_without_first_frame(msg);
91 if (RuntimeOption::EvalHardTypeHints) {
92 raise_error("Error handler tried to recover from typehint violation");
96 void raise_return_typehint_error(const std::string& msg) {
97 if (RuntimeOption::PHP7_EngineExceptions) {
98 VMRegAnchor _;
99 SystemLib::throwTypeErrorObject(msg);
101 raise_recoverable_error(msg);
102 if (RuntimeOption::EvalCheckReturnTypeHints >= 3) {
103 raise_error("Error handler tried to recover from a return typehint "
104 "violation");
108 void raise_disallowed_dynamic_call(const Func* f) {
109 raise_hack_strict(
110 RuntimeOption::DisallowDynamicVarEnvFuncs,
111 "disallow_dynamic_var_env_funcs",
112 Strings::DISALLOWED_DYNCALL, f->fullName()->data()
116 void raise_intish_index_cast() {
117 if (UNLIKELY(RID().getSuppressHackArrayCompatNotices())) return;
118 raise_notice("Hack Array Compat: Intish index cast");
121 void raise_hackarr_compat_notice(const std::string& msg) {
122 if (UNLIKELY(RID().getSuppressHackArrayCompatNotices())) return;
123 raise_notice("Hack Array Compat: %s", msg.c_str());
126 namespace {
128 void raise_hackarr_type_hint_impl(const Func* func,
129 const ArrayData* ad,
130 AnnotType at,
131 folly::Optional<int> param) {
132 if (UNLIKELY(RID().getSuppressHackArrayCompatNotices())) return;
134 auto const name = [&]{
135 if (at == AnnotType::VArray) return "varray";
136 if (at == AnnotType::DArray) return "darray";
137 if (at == AnnotType::VArrOrDArr) return "varray_or_darray";
138 if (at == AnnotType::Array) return "array";
139 always_assert(false);
140 }();
142 auto const given = [&]{
143 if (ad->isVArray()) return "varray";
144 if (ad->isDArray()) return "darray";
145 return "array";
146 }();
148 if (param) {
149 raise_notice(
150 "Hack Array Compat: Argument %d to %s() must be of type %s, %s given",
151 *param + 1, func->fullDisplayName()->data(), name, given
153 } else {
154 raise_notice(
155 "Hack Array Compat: Value returned from %s() must be of type %s, "
156 "%s given",
157 func->fullDisplayName()->data(), name, given
164 void raise_hackarr_type_hint_param_notice(const Func* func,
165 const ArrayData* ad,
166 AnnotType at,
167 int param) {
168 raise_hackarr_type_hint_impl(func, ad, at, param);
171 void raise_hackarr_type_hint_ret_notice(const Func* func,
172 const ArrayData* ad,
173 AnnotType at) {
174 raise_hackarr_type_hint_impl(func, ad, at, folly::none);
177 void raise_hackarr_type_hint_outparam_notice(const Func* func,
178 const ArrayData* ad,
179 AnnotType at,
180 int param) {
181 if (UNLIKELY(RID().getSuppressHackArrayCompatNotices())) return;
183 auto const name = [&]{
184 if (at == AnnotType::VArray) return "varray";
185 if (at == AnnotType::DArray) return "darray";
186 if (at == AnnotType::VArrOrDArr) return "varray_or_darray";
187 if (at == AnnotType::Array) return "array";
188 always_assert(false);
189 }();
191 auto const given = [&]{
192 if (ad->isVArray()) return "varray";
193 if (ad->isDArray()) return "darray";
194 return "array";
195 }();
197 raise_notice(
198 "Hack Array Compat: Argument %d returned from %s() as an inout parameter "
199 "must be of type %s, %s given",
200 param + 1, func->fullDisplayName()->data(), name, given
204 void raise_call_to_undefined(const StringData* name, const Class* cls) {
205 if (LIKELY(!needsStripInOut(name))) {
206 if (cls) {
207 raise_error("Call to undefined method %s::%s()", cls->name()->data(),
208 name->data());
210 raise_error("Call to undefined function %s()", name->data());
211 } else {
212 auto stripped = stripInOutSuffix(name);
213 if (cls) {
214 if (cls->lookupMethod(stripped)) {
215 raise_error("Call to method %s::%s() with incorrectly annotated inout "
216 "parameter", cls->name()->data(), stripped->data());
218 raise_error("Call to undefined method %s::%s()", cls->name()->data(),
219 stripped->data());
220 } else if (Unit::lookupFunc(stripped)) {
221 raise_error("Call to function %s() with incorrectly annotated inout "
222 "parameter", stripped->data());
224 raise_error("Call to undefined function %s()", stripped->data());
228 void raise_recoverable_error(const char *fmt, ...) {
229 std::string msg;
230 va_list ap;
231 va_start(ap, fmt);
232 string_vsnprintf(msg, fmt, ap);
233 va_end(ap);
234 raise_recoverable_error(msg);
237 static int64_t g_notice_counter = 0;
239 static bool notice_freq_check(ErrorMode mode) {
240 if (!g_context->getThrowAllErrors() &&
241 (RuntimeOption::NoticeFrequency <= 0 ||
242 g_notice_counter++ % RuntimeOption::NoticeFrequency != 0)) {
243 return false;
245 return g_context->errorNeedsHandling(
246 static_cast<int>(mode), true, ExecutionContext::ErrorThrowMode::Never);
249 #define HANDLE_ERROR(userHandle, throwMode, str, skip) \
250 g_context->handleError(msg, static_cast<int>(mode), userHandle, \
251 ExecutionContext::ErrorThrowMode::throwMode, \
252 str, \
253 skip);
255 static void raise_notice_helper(ErrorMode mode, bool skipTop,
256 const std::string& msg) {
257 switch (mode) {
258 case ErrorMode::STRICT:
259 HANDLE_ERROR(true, Never, "\nStrict Warning: ", skipTop);
260 break;
261 case ErrorMode::NOTICE:
262 HANDLE_ERROR(true, Never, "\nNotice: ", skipTop);
263 break;
264 case ErrorMode::PHP_DEPRECATED:
265 HANDLE_ERROR(true, Never, "\nDeprecated: ", skipTop);
266 break;
267 default:
268 always_assert(!"Unhandled type of error");
272 void raise_strict_warning(const std::string &msg) {
273 if (notice_freq_check(ErrorMode::STRICT)) {
274 raise_notice_helper(ErrorMode::STRICT, false, msg);
278 void raise_strict_warning_without_first_frame(const std::string &msg) {
279 if (notice_freq_check(ErrorMode::STRICT)) {
280 raise_notice_helper(ErrorMode::STRICT, true, msg);
284 void raise_strict_warning(const char *fmt, ...) {
285 if (!notice_freq_check(ErrorMode::STRICT)) return;
287 std::string msg;
288 va_list ap;
289 va_start(ap, fmt);
290 string_vsnprintf(msg, fmt, ap);
291 va_end(ap);
292 raise_notice_helper(ErrorMode::STRICT, false, msg);
295 static int64_t g_warning_counter = 0;
297 bool warning_freq_check() {
298 if (!g_context->getThrowAllErrors() &&
299 (RuntimeOption::WarningFrequency <= 0 ||
300 g_warning_counter++ % RuntimeOption::WarningFrequency != 0)) {
301 return false;
303 return g_context->errorNeedsHandling(
304 static_cast<int>(ErrorMode::WARNING), true,
305 ExecutionContext::ErrorThrowMode::Never);
308 void raise_warning_helper(bool skipTop, const std::string& msg) {
309 auto mode = ErrorMode::WARNING;
310 HANDLE_ERROR(true, Never, "\nWarning: ", skipTop);
313 void raise_warning(const std::string &msg) {
314 if (warning_freq_check()) {
315 raise_warning_helper(false, msg);
319 void raise_warning_without_first_frame(const std::string &msg) {
320 if (warning_freq_check()) {
321 raise_warning_helper(true, msg);
325 void raise_warning(const char *fmt, ...) {
326 if (!warning_freq_check()) return;
327 std::string msg;
328 va_list ap;
329 va_start(ap, fmt);
330 string_vsnprintf(msg, fmt, ap);
331 va_end(ap);
332 raise_warning_helper(false, msg);
335 static void raise_hack_strict_helper(
336 HackStrictOption option, const char *ini_setting, const std::string& msg) {
337 if (option == HackStrictOption::WARN) {
338 raise_warning_helper(
339 false, std::string("(hhvm.hack.") + ini_setting + "=warn) " + msg);
340 } else if (option == HackStrictOption::ON) {
341 raise_error(std::string("(hhvm.hack.") + ini_setting + "=error) " + msg);
347 * For use with the HackStrictOption settings. This will warn, error, or do
348 * nothing depending on what the user chose for the option. The second param
349 * should be the ini setting name after "hhvm.hack."
351 void raise_hack_strict(HackStrictOption option, const char *ini_setting,
352 const std::string& msg) {
353 if (option == HackStrictOption::WARN ?
354 !warning_freq_check() : (option != HackStrictOption::ON)) {
355 return;
357 raise_hack_strict_helper(option, ini_setting, msg);
360 void raise_hack_strict(HackStrictOption option, const char *ini_setting,
361 const char *fmt, ...) {
362 if (option == HackStrictOption::WARN ?
363 !warning_freq_check() : (option != HackStrictOption::ON)) {
364 return;
366 std::string msg;
367 va_list ap;
368 va_start(ap, fmt);
369 string_vsnprintf(msg, fmt, ap);
370 va_end(ap);
371 raise_hack_strict_helper(option, ini_setting, msg);
375 * Warnings are currently sampled. raise_warning_unsampled can help when
376 * migrating warnings to errors.
378 * In general, RaiseDebuggingFrequency should be kept at 1.
380 static int64_t g_raise_warning_unsampled_counter = 0;
382 void raise_warning_unsampled(const std::string &msg) {
383 if (RuntimeOption::RaiseDebuggingFrequency <= 0 ||
384 (g_raise_warning_unsampled_counter++) %
385 RuntimeOption::RaiseDebuggingFrequency != 0) {
386 return;
388 if (g_context->errorNeedsHandling(
389 static_cast<int>(ErrorMode::WARNING), true,
390 ExecutionContext::ErrorThrowMode::Never)) {
391 raise_warning_helper(false, msg);
395 void raise_warning_unsampled(const char *fmt, ...) {
396 std::string msg;
397 va_list ap;
398 va_start(ap, fmt);
399 string_vsnprintf(msg, fmt, ap);
400 va_end(ap);
401 raise_warning_unsampled(msg);
404 void raise_notice(const std::string &msg) {
405 if (notice_freq_check(ErrorMode::NOTICE)) {
406 raise_notice_helper(ErrorMode::NOTICE, false, msg);
410 void raise_notice_without_first_frame(const std::string &msg) {
411 if (notice_freq_check(ErrorMode::NOTICE)) {
412 raise_notice_helper(ErrorMode::NOTICE, true, msg);
416 void raise_notice(const char *fmt, ...) {
417 if (!notice_freq_check(ErrorMode::NOTICE)) return;
418 std::string msg;
419 va_list ap;
420 va_start(ap, fmt);
421 string_vsnprintf(msg, fmt, ap);
422 va_end(ap);
423 raise_notice_helper(ErrorMode::NOTICE, false, msg);
426 void raise_deprecated(const std::string &msg) {
427 if (notice_freq_check(ErrorMode::PHP_DEPRECATED)) {
428 raise_notice_helper(ErrorMode::PHP_DEPRECATED, false, msg);
432 void raise_deprecated_without_first_frame(const std::string &msg) {
433 if (notice_freq_check(ErrorMode::PHP_DEPRECATED)) {
434 raise_notice_helper(ErrorMode::PHP_DEPRECATED, true, msg);
438 void raise_deprecated(const char *fmt, ...) {
439 if (!notice_freq_check(ErrorMode::PHP_DEPRECATED)) return;
440 std::string msg;
441 va_list ap;
442 va_start(ap, fmt);
443 string_vsnprintf(msg, fmt, ap);
444 va_end(ap);
445 raise_notice_helper(ErrorMode::PHP_DEPRECATED, false, msg);
448 void raise_param_type_warning(
449 const char* func_name,
450 int param_num,
451 DataType expected_type,
452 DataType actual_type) {
454 // its ok to do this before munging, because it only looks at the
455 // end of the string
456 auto is_constructor = is_constructor_name(func_name);
457 if (!is_constructor && !warning_freq_check()) return;
458 // slice off fg1_
459 if (strncmp(func_name, "fg1_", 4) == 0) {
460 func_name += 4;
461 } else if (strncmp(func_name, "tg1_", 4) == 0) {
462 func_name += 4;
464 assertx(param_num > 0);
465 auto msg = folly::sformat(
466 "{}() expects parameter {} to be {}, {} given",
467 func_name,
468 param_num,
469 getDataTypeString(expected_type).data(),
470 getDataTypeString(actual_type).data());
472 if (is_constructor) {
473 SystemLib::throwExceptionObject(msg);
476 raise_warning_helper(false, msg);
479 void raise_message(ErrorMode mode,
480 const char *fmt,
481 va_list ap) {
482 std::string msg;
483 string_vsnprintf(msg, fmt, ap);
484 raise_message(mode, false, msg);
487 void raise_message(ErrorMode mode,
488 const char *fmt,
489 ...) {
490 std::string msg;
491 va_list ap;
492 va_start(ap, fmt);
493 string_vsnprintf(msg, fmt, ap);
494 va_end(ap);
495 raise_message(mode, false, msg);
498 void raise_message(ErrorMode mode,
499 bool skipTop,
500 const std::string &msg) {
501 if (mode == ErrorMode::ERROR) {
502 HANDLE_ERROR(false, Always, "\nFatal error: ", skipTop);
503 not_reached();
506 if (mode == ErrorMode::RECOVERABLE_ERROR) {
507 HANDLE_ERROR(true, IfUnhandled, "\nCatchable fatal error: ", skipTop);
508 return;
511 if (!g_context->errorNeedsHandling(static_cast<int>(mode), true,
512 ExecutionContext::ErrorThrowMode::Never)) {
513 return;
516 if (mode == ErrorMode::WARNING) {
517 if (RuntimeOption::WarningFrequency <= 0 ||
518 (g_warning_counter++) % RuntimeOption::WarningFrequency != 0) {
519 return;
521 HANDLE_ERROR(true, Never, "\nWarning: ", skipTop);
522 return;
525 if (RuntimeOption::NoticeFrequency <= 0 ||
526 (g_notice_counter++) % RuntimeOption::NoticeFrequency != 0) {
527 return;
530 raise_notice_helper(mode, skipTop, msg);
533 ///////////////////////////////////////////////////////////////////////////////
535 SuppressHackArrCompatNotices::SuppressHackArrCompatNotices()
536 : old{RID().getSuppressHackArrayCompatNotices()} {
537 RID().setSuppressHackArrayCompatNotices(true);
540 SuppressHackArrCompatNotices::~SuppressHackArrCompatNotices() {
541 RID().setSuppressHackArrayCompatNotices(old);
544 ///////////////////////////////////////////////////////////////////////////////