Add support for HHBC ops with 5 immediates
[hiphop-php.git] / hphp / runtime / base / runtime-error.cpp
blob9c280484349e0b6293232f85f0b092b4b7af6f90
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());
127 void raise_hack_arr_compat_serialize_notice(const ArrayData* arr) {
128 if (UNLIKELY(RID().getSuppressHackArrayCompatNotices())) return;
129 auto const type = [&]{
130 if (arr->isVecArray()) return "vec";
131 if (arr->isDict()) return "dict";
132 if (arr->isKeyset()) return "keyset";
133 return "array";
134 }();
135 raise_notice("Hack Array Compat: Serializing %s", type);
138 namespace {
140 void raise_hackarr_type_hint_impl(const Func* func,
141 const ArrayData* ad,
142 AnnotType at,
143 folly::Optional<int> param) {
144 if (UNLIKELY(RID().getSuppressHackArrayCompatNotices())) return;
146 auto const name = [&]{
147 if (at == AnnotType::VArray) return "varray";
148 if (at == AnnotType::DArray) return "darray";
149 if (at == AnnotType::VArrOrDArr) return "varray_or_darray";
150 if (at == AnnotType::Array) return "array";
151 always_assert(false);
152 }();
154 auto const given = [&]{
155 if (ad->isVArray()) return "varray";
156 if (ad->isDArray()) return "darray";
157 return "array";
158 }();
160 if (param) {
161 raise_notice(
162 "Hack Array Compat: Argument %d to %s() must be of type %s, %s given",
163 *param + 1, func->fullDisplayName()->data(), name, given
165 } else {
166 raise_notice(
167 "Hack Array Compat: Value returned from %s() must be of type %s, "
168 "%s given",
169 func->fullDisplayName()->data(), name, given
176 void raise_hackarr_type_hint_param_notice(const Func* func,
177 const ArrayData* ad,
178 AnnotType at,
179 int param) {
180 raise_hackarr_type_hint_impl(func, ad, at, param);
183 void raise_hackarr_type_hint_ret_notice(const Func* func,
184 const ArrayData* ad,
185 AnnotType at) {
186 raise_hackarr_type_hint_impl(func, ad, at, folly::none);
189 void raise_hackarr_type_hint_outparam_notice(const Func* func,
190 const ArrayData* ad,
191 AnnotType at,
192 int param) {
193 if (UNLIKELY(RID().getSuppressHackArrayCompatNotices())) return;
195 auto const name = [&]{
196 if (at == AnnotType::VArray) return "varray";
197 if (at == AnnotType::DArray) return "darray";
198 if (at == AnnotType::VArrOrDArr) return "varray_or_darray";
199 if (at == AnnotType::Array) return "array";
200 always_assert(false);
201 }();
203 auto const given = [&]{
204 if (ad->isVArray()) return "varray";
205 if (ad->isDArray()) return "darray";
206 return "array";
207 }();
209 raise_notice(
210 "Hack Array Compat: Argument %d returned from %s() as an inout parameter "
211 "must be of type %s, %s given",
212 param + 1, func->fullDisplayName()->data(), name, given
216 void raise_call_to_undefined(const StringData* name, const Class* cls) {
217 if (LIKELY(!needsStripInOut(name))) {
218 if (cls) {
219 raise_error("Call to undefined method %s::%s()", cls->name()->data(),
220 name->data());
222 raise_error("Call to undefined function %s()", name->data());
223 } else {
224 auto stripped = stripInOutSuffix(name);
225 if (cls) {
226 if (cls->lookupMethod(stripped)) {
227 raise_error("Call to method %s::%s() with incorrectly annotated inout "
228 "parameter", cls->name()->data(), stripped->data());
230 raise_error("Call to undefined method %s::%s()", cls->name()->data(),
231 stripped->data());
232 } else if (Unit::lookupFunc(stripped)) {
233 raise_error("Call to function %s() with incorrectly annotated inout "
234 "parameter", stripped->data());
236 raise_error("Call to undefined function %s()", stripped->data());
240 void raise_recoverable_error(const char *fmt, ...) {
241 std::string msg;
242 va_list ap;
243 va_start(ap, fmt);
244 string_vsnprintf(msg, fmt, ap);
245 va_end(ap);
246 raise_recoverable_error(msg);
249 static int64_t g_notice_counter = 0;
251 static bool notice_freq_check(ErrorMode mode) {
252 if (!g_context->getThrowAllErrors() &&
253 (RuntimeOption::NoticeFrequency <= 0 ||
254 g_notice_counter++ % RuntimeOption::NoticeFrequency != 0)) {
255 return false;
257 return g_context->errorNeedsHandling(
258 static_cast<int>(mode), true, ExecutionContext::ErrorThrowMode::Never);
261 #define HANDLE_ERROR(userHandle, throwMode, str, skip) \
262 g_context->handleError(msg, static_cast<int>(mode), userHandle, \
263 ExecutionContext::ErrorThrowMode::throwMode, \
264 str, \
265 skip);
267 static void raise_notice_helper(ErrorMode mode, bool skipTop,
268 const std::string& msg) {
269 switch (mode) {
270 case ErrorMode::STRICT:
271 HANDLE_ERROR(true, Never, "\nStrict Warning: ", skipTop);
272 break;
273 case ErrorMode::NOTICE:
274 HANDLE_ERROR(true, Never, "\nNotice: ", skipTop);
275 break;
276 case ErrorMode::PHP_DEPRECATED:
277 HANDLE_ERROR(true, Never, "\nDeprecated: ", skipTop);
278 break;
279 default:
280 always_assert(!"Unhandled type of error");
284 void raise_strict_warning(const std::string &msg) {
285 if (notice_freq_check(ErrorMode::STRICT)) {
286 raise_notice_helper(ErrorMode::STRICT, false, msg);
290 void raise_strict_warning_without_first_frame(const std::string &msg) {
291 if (notice_freq_check(ErrorMode::STRICT)) {
292 raise_notice_helper(ErrorMode::STRICT, true, msg);
296 void raise_strict_warning(const char *fmt, ...) {
297 if (!notice_freq_check(ErrorMode::STRICT)) return;
299 std::string msg;
300 va_list ap;
301 va_start(ap, fmt);
302 string_vsnprintf(msg, fmt, ap);
303 va_end(ap);
304 raise_notice_helper(ErrorMode::STRICT, false, msg);
307 static int64_t g_warning_counter = 0;
309 bool warning_freq_check() {
310 if (!g_context->getThrowAllErrors() &&
311 (RuntimeOption::WarningFrequency <= 0 ||
312 g_warning_counter++ % RuntimeOption::WarningFrequency != 0)) {
313 return false;
315 return g_context->errorNeedsHandling(
316 static_cast<int>(ErrorMode::WARNING), true,
317 ExecutionContext::ErrorThrowMode::Never);
320 void raise_warning_helper(bool skipTop, const std::string& msg) {
321 auto mode = ErrorMode::WARNING;
322 HANDLE_ERROR(true, Never, "\nWarning: ", skipTop);
325 void raise_warning(const std::string &msg) {
326 if (warning_freq_check()) {
327 raise_warning_helper(false, msg);
331 void raise_warning_without_first_frame(const std::string &msg) {
332 if (warning_freq_check()) {
333 raise_warning_helper(true, msg);
337 void raise_warning(const char *fmt, ...) {
338 if (!warning_freq_check()) return;
339 std::string msg;
340 va_list ap;
341 va_start(ap, fmt);
342 string_vsnprintf(msg, fmt, ap);
343 va_end(ap);
344 raise_warning_helper(false, msg);
347 static void raise_hack_strict_helper(
348 HackStrictOption option, const char *ini_setting, const std::string& msg) {
349 if (option == HackStrictOption::WARN) {
350 raise_warning_helper(
351 false, std::string("(hhvm.hack.") + ini_setting + "=warn) " + msg);
352 } else if (option == HackStrictOption::ON) {
353 raise_error(std::string("(hhvm.hack.") + ini_setting + "=error) " + msg);
359 * For use with the HackStrictOption settings. This will warn, error, or do
360 * nothing depending on what the user chose for the option. The second param
361 * should be the ini setting name after "hhvm.hack."
363 void raise_hack_strict(HackStrictOption option, const char *ini_setting,
364 const std::string& msg) {
365 if (option == HackStrictOption::WARN ?
366 !warning_freq_check() : (option != HackStrictOption::ON)) {
367 return;
369 raise_hack_strict_helper(option, ini_setting, msg);
372 void raise_hack_strict(HackStrictOption option, const char *ini_setting,
373 const char *fmt, ...) {
374 if (option == HackStrictOption::WARN ?
375 !warning_freq_check() : (option != HackStrictOption::ON)) {
376 return;
378 std::string msg;
379 va_list ap;
380 va_start(ap, fmt);
381 string_vsnprintf(msg, fmt, ap);
382 va_end(ap);
383 raise_hack_strict_helper(option, ini_setting, msg);
387 * Warnings are currently sampled. raise_warning_unsampled can help when
388 * migrating warnings to errors.
390 * In general, RaiseDebuggingFrequency should be kept at 1.
392 static int64_t g_raise_warning_unsampled_counter = 0;
394 void raise_warning_unsampled(const std::string &msg) {
395 if (RuntimeOption::RaiseDebuggingFrequency <= 0 ||
396 (g_raise_warning_unsampled_counter++) %
397 RuntimeOption::RaiseDebuggingFrequency != 0) {
398 return;
400 if (g_context->errorNeedsHandling(
401 static_cast<int>(ErrorMode::WARNING), true,
402 ExecutionContext::ErrorThrowMode::Never)) {
403 raise_warning_helper(false, msg);
407 void raise_warning_unsampled(const char *fmt, ...) {
408 std::string msg;
409 va_list ap;
410 va_start(ap, fmt);
411 string_vsnprintf(msg, fmt, ap);
412 va_end(ap);
413 raise_warning_unsampled(msg);
416 void raise_notice(const std::string &msg) {
417 if (notice_freq_check(ErrorMode::NOTICE)) {
418 raise_notice_helper(ErrorMode::NOTICE, false, msg);
422 void raise_notice_without_first_frame(const std::string &msg) {
423 if (notice_freq_check(ErrorMode::NOTICE)) {
424 raise_notice_helper(ErrorMode::NOTICE, true, msg);
428 void raise_notice(const char *fmt, ...) {
429 if (!notice_freq_check(ErrorMode::NOTICE)) return;
430 std::string msg;
431 va_list ap;
432 va_start(ap, fmt);
433 string_vsnprintf(msg, fmt, ap);
434 va_end(ap);
435 raise_notice_helper(ErrorMode::NOTICE, false, msg);
438 void raise_deprecated(const std::string &msg) {
439 if (notice_freq_check(ErrorMode::PHP_DEPRECATED)) {
440 raise_notice_helper(ErrorMode::PHP_DEPRECATED, false, msg);
444 void raise_deprecated_without_first_frame(const std::string &msg) {
445 if (notice_freq_check(ErrorMode::PHP_DEPRECATED)) {
446 raise_notice_helper(ErrorMode::PHP_DEPRECATED, true, msg);
450 void raise_deprecated(const char *fmt, ...) {
451 if (!notice_freq_check(ErrorMode::PHP_DEPRECATED)) return;
452 std::string msg;
453 va_list ap;
454 va_start(ap, fmt);
455 string_vsnprintf(msg, fmt, ap);
456 va_end(ap);
457 raise_notice_helper(ErrorMode::PHP_DEPRECATED, false, msg);
460 void raise_param_type_warning(
461 const char* func_name,
462 int param_num,
463 DataType expected_type,
464 DataType actual_type) {
466 // its ok to do this before munging, because it only looks at the
467 // end of the string
468 auto is_constructor = is_constructor_name(func_name);
469 if (!is_constructor && !warning_freq_check()) return;
470 // slice off fg1_
471 if (strncmp(func_name, "fg1_", 4) == 0) {
472 func_name += 4;
473 } else if (strncmp(func_name, "tg1_", 4) == 0) {
474 func_name += 4;
476 assertx(param_num > 0);
477 auto msg = folly::sformat(
478 "{}() expects parameter {} to be {}, {} given",
479 func_name,
480 param_num,
481 getDataTypeString(expected_type).data(),
482 getDataTypeString(actual_type).data());
484 if (is_constructor) {
485 SystemLib::throwExceptionObject(msg);
488 raise_warning_helper(false, msg);
491 void raise_message(ErrorMode mode,
492 const char *fmt,
493 va_list ap) {
494 std::string msg;
495 string_vsnprintf(msg, fmt, ap);
496 raise_message(mode, false, msg);
499 void raise_message(ErrorMode mode,
500 const char *fmt,
501 ...) {
502 std::string msg;
503 va_list ap;
504 va_start(ap, fmt);
505 string_vsnprintf(msg, fmt, ap);
506 va_end(ap);
507 raise_message(mode, false, msg);
510 void raise_message(ErrorMode mode,
511 bool skipTop,
512 const std::string &msg) {
513 if (mode == ErrorMode::ERROR) {
514 HANDLE_ERROR(false, Always, "\nFatal error: ", skipTop);
515 not_reached();
518 if (mode == ErrorMode::RECOVERABLE_ERROR) {
519 HANDLE_ERROR(true, IfUnhandled, "\nCatchable fatal error: ", skipTop);
520 return;
523 if (!g_context->errorNeedsHandling(static_cast<int>(mode), true,
524 ExecutionContext::ErrorThrowMode::Never)) {
525 return;
528 if (mode == ErrorMode::WARNING) {
529 if (RuntimeOption::WarningFrequency <= 0 ||
530 (g_warning_counter++) % RuntimeOption::WarningFrequency != 0) {
531 return;
533 HANDLE_ERROR(true, Never, "\nWarning: ", skipTop);
534 return;
537 if (RuntimeOption::NoticeFrequency <= 0 ||
538 (g_notice_counter++) % RuntimeOption::NoticeFrequency != 0) {
539 return;
542 raise_notice_helper(mode, skipTop, msg);
545 ///////////////////////////////////////////////////////////////////////////////
547 SuppressHackArrCompatNotices::SuppressHackArrCompatNotices()
548 : old{RID().getSuppressHackArrayCompatNotices()} {
549 RID().setSuppressHackArrayCompatNotices(true);
552 SuppressHackArrCompatNotices::~SuppressHackArrCompatNotices() {
553 RID().setSuppressHackArrayCompatNotices(old);
556 ///////////////////////////////////////////////////////////////////////////////