Remove empty from slow runtime tests
[hiphop-php.git] / hphp / runtime / base / exceptions.cpp
blob4ec963d327b2d8f033d9bfe9f134c4cd0246ce1a
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/exceptions.h"
18 #include "hphp/system/systemlib.h"
19 #include "hphp/runtime/base/backtrace.h"
20 #include "hphp/runtime/base/execution-context.h"
21 #include "hphp/runtime/base/object-data.h"
22 #include "hphp/runtime/base/type-variant.h"
23 #include "hphp/runtime/ext/std/ext_std_errorfunc.h"
24 #include "hphp/runtime/vm/vm-regs.h"
26 namespace HPHP {
28 //////////////////////////////////////////////////////////////////////
30 const StaticString s_file("file");
31 const StaticString s_line("line");
32 const StaticString s_trace("trace");
33 const StaticString s_traceOpts("traceOpts");
34 const StaticString s___toString("__toString");
35 const Slot s_fileIdx{3};
36 const Slot s_lineIdx{4};
37 const Slot s_traceIdx{5};
38 const Slot s_traceOptsIdx{0};
40 RDS_LOCAL(int, rl_exit_code);
42 ExtendedException::ExtendedException() : Exception() {
43 computeBacktrace();
46 ExtendedException::ExtendedException(const std::string &msg) {
47 m_msg = msg;
48 computeBacktrace();
51 ExtendedException::ExtendedException(SkipFrame, const std::string &msg) {
52 m_msg = msg;
53 computeBacktrace(true);
56 ExtendedException::ExtendedException(const char *fmt, ...) {
57 va_list ap;
58 va_start(ap, fmt);
59 format(fmt, ap);
60 va_end(ap);
61 computeBacktrace();
64 ExtendedException::ExtendedException(const std::string& msg,
65 ArrayData* backTrace)
66 : m_btp(backTrace)
68 m_msg = msg;
72 * Normally we wouldn't need an explicit copy/move-constructors, or
73 * copy/move-assignment operators, but we have to make sure m_key isn't copied.
76 ExtendedException::ExtendedException(const ExtendedException& other)
77 : Exception(other),
78 m_btp(other.m_btp),
79 m_silent(other.m_silent)
82 ExtendedException::ExtendedException(ExtendedException&& other) noexcept
83 : Exception(std::move(other)),
84 m_btp(std::move(other.m_btp)),
85 m_silent(other.m_silent)
88 ExtendedException&
89 ExtendedException::operator=(const ExtendedException& other) {
90 Exception::operator=(other);
91 m_btp = other.m_btp;
92 m_silent = other.m_silent;
93 return *this;
96 ExtendedException&
97 ExtendedException::operator=(ExtendedException&& other) noexcept {
98 Exception::operator=(std::move(other));
99 m_btp = std::move(other.m_btp);
100 m_silent = other.m_silent;
101 return *this;
104 Array ExtendedException::getBacktrace() const {
105 return Array(m_btp.get());
108 std::pair<String, int> ExtendedException::getFileAndLine() const {
109 String file = empty_string();
110 int line = 0;
111 Array bt = getBacktrace();
112 if (!bt.empty()) {
113 Array top = tvCastToArrayLike(bt.rvalAt(0).tv());
114 if (top.exists(s_file)) file = tvCastToString(top.rvalAt(s_file).tv());
115 if (top.exists(s_line)) line = tvCastToInt64(top.rvalAt(s_line).tv());
117 return std::make_pair(file, line);
120 void ExtendedException::computeBacktrace(bool skipFrame /* = false */) {
121 Array bt = createBacktrace(BacktraceArgs()
122 .skipTop(skipFrame)
123 .withSelf()
124 .withMetadata());
125 m_btp = bt.get();
128 void ExtendedException::recomputeBacktraceFromWH(c_WaitableWaitHandle* wh) {
129 assertx(wh);
130 Array bt = createBacktrace(BacktraceArgs()
131 .fromWaitHandle(wh)
132 .withSelf()
133 .withMetadata());
134 m_btp = bt.get();
137 //////////////////////////////////////////////////////////////////////
139 FatalErrorException::FatalErrorException(int, const char *msg, ...) {
140 va_list ap;
141 va_start(ap, msg);
142 format(msg, ap);
143 va_end(ap);
144 computeBacktrace();
147 FatalErrorException::FatalErrorException(const std::string& msg,
148 const Array& backtrace,
149 bool isRecoverable /* = false */)
150 : ExtendedException(msg, backtrace.get()), m_recoverable(isRecoverable)
153 //////////////////////////////////////////////////////////////////////
155 void throw_null_pointer_exception() {
156 throw ExtendedException("A null object pointer was used.");
159 void throw_invalid_object_type(const char* clsName) {
160 throw ExtendedException("Unexpected object type %s.", clsName);
163 void throw_not_implemented(const char* feature) {
164 throw ExtendedException("%s is not implemented yet.", feature);
167 void throw_not_supported(const char* feature, const char* reason) {
168 throw ExtendedException("%s is not supported: %s", feature, reason);
171 ///////////////////////////////////////////////////////////////////////////////
173 [[noreturn]]
174 void raise_fatal_error(const char* msg,
175 const Array& bt /* = null_array */,
176 bool recoverable /* = false */,
177 bool silent /* = false */,
178 bool throws /* = true */) {
179 if (RuntimeOption::PHP7_EngineExceptions && throws) {
180 VMRegAnchor _;
181 SystemLib::throwErrorObject(Variant(msg));
183 auto ex = bt.isNull() && !recoverable
184 ? FatalErrorException(msg)
185 : FatalErrorException(msg, bt, recoverable);
186 ex.setSilent(silent);
187 throw ex;
190 ///////////////////////////////////////////////////////////////////////////////
192 namespace {
193 DEBUG_ONLY bool vmfp_is_builtin() {
194 VMRegAnchor _;
195 auto const fp = vmfp();
196 return fp && fp->func()->isBuiltin();
199 DEBUG_ONLY bool is_throwable(ObjectData* throwable) {
200 auto const erCls = SystemLib::s_ErrorClass;
201 auto const exCls = SystemLib::s_ExceptionClass;
202 return throwable->instanceof(erCls) || throwable->instanceof(exCls);
205 DEBUG_ONLY bool throwable_has_expected_props() {
206 auto const erCls = SystemLib::s_ErrorClass;
207 auto const exCls = SystemLib::s_ExceptionClass;
208 if (erCls->lookupDeclProp(s_file.get()) != s_fileIdx ||
209 exCls->lookupDeclProp(s_file.get()) != s_fileIdx ||
210 erCls->lookupDeclProp(s_line.get()) != s_lineIdx ||
211 exCls->lookupDeclProp(s_line.get()) != s_lineIdx ||
212 erCls->lookupDeclProp(s_trace.get()) != s_traceIdx ||
213 exCls->lookupDeclProp(s_trace.get()) != s_traceIdx) {
214 return false;
216 // Check that we don't have the expected type-hints on these props so we
217 // don't need to verify anything.
218 return
219 erCls->declPropTypeConstraint(s_fileIdx).isString() &&
220 exCls->declPropTypeConstraint(s_fileIdx).isString() &&
221 erCls->declPropTypeConstraint(s_lineIdx).isInt() &&
222 exCls->declPropTypeConstraint(s_lineIdx).isInt() &&
223 !erCls->declPropTypeConstraint(s_traceIdx).isCheckable() &&
224 !exCls->declPropTypeConstraint(s_traceIdx).isCheckable();
227 int64_t exception_get_trace_options() {
228 auto const exCls = SystemLib::s_ExceptionClass;
229 assertx(exCls->lookupSProp(s_traceOpts.get()) == s_traceOptsIdx);
230 assertx(exCls->needInitialization());
232 exCls->initialize();
233 auto const traceOptsTV = exCls->getSPropData(s_traceOptsIdx);
234 return traceOptsTV->m_type == KindOfInt64
235 ? traceOptsTV->m_data.num : 0;
238 void throwable_init_file_and_line_from_trace(ObjectData* throwable) {
239 assertx(is_throwable(throwable));
240 assertx(throwable_has_expected_props());
242 auto const trace_rval = throwable->propRvalAtOffset(s_traceIdx);
244 if (trace_rval.type() == KindOfResource) {
245 auto bt = dyn_cast<CompactTrace>(Resource(trace_rval.val().pres));
246 assertx(bt);
248 for (auto& f : bt->frames()) {
249 if (!f.func || f.func->isBuiltin()) continue;
251 auto const ln = f.func->unit()->getLineNumber(f.prevPc);
252 tvSetIgnoreRef(
253 make_tv<KindOfInt64>(ln),
254 throwable->propLvalAtOffset(s_lineIdx)
257 if (auto fn = f.func->originalFilename()) {
258 tvSetIgnoreRef(
259 make_tv<KindOfPersistentString>(fn),
260 throwable->propLvalAtOffset(s_fileIdx)
262 } else {
263 tvSetIgnoreRef(
264 make_tv<KindOfPersistentString>(f.func->unit()->filepath()),
265 throwable->propLvalAtOffset(s_fileIdx)
268 return;
270 return;
273 assertx(RuntimeOption::EvalHackArrDVArrs ?
274 isVecType(trace_rval.type()) :
275 isArrayType(trace_rval.type())
277 auto const trace = trace_rval.val().parr;
278 for (ArrayIter iter(trace); iter; ++iter) {
279 assertx(iter.second().asTypedValue()->m_type == (
280 RuntimeOption::EvalHackArrDVArrs ? KindOfDict : KindOfArray
282 auto const frame = iter.second().asTypedValue()->m_data.parr;
283 auto const file = frame->rval(s_file.get());
284 auto const line = frame->rval(s_line.get());
285 if (file || line) {
286 if (file) {
287 auto const tv = file.tv();
288 tvSetIgnoreRef(
289 tvAssertCell(tv),
290 throwable->propLvalAtOffset(s_fileIdx)
293 if (line) {
294 auto const tv = line.tv();
295 tvSetIgnoreRef(
296 tvAssertCell(tv),
297 throwable->propLvalAtOffset(s_lineIdx)
300 return;
306 void throwable_init(ObjectData* throwable) {
307 assertx(is_throwable(throwable));
308 assertx(throwable_has_expected_props());
310 auto const trace_lval = throwable->propLvalAtOffset(s_traceIdx);
311 auto opts = exception_get_trace_options();
312 auto const filterOpts = opts & ~k_DEBUG_BACKTRACE_IGNORE_ARGS;
313 if (
314 !RuntimeOption::EvalEnableCompactBacktrace || filterOpts ||
315 (RuntimeOption::EnableArgsInBacktraces &&
316 opts != k_DEBUG_BACKTRACE_IGNORE_ARGS)
318 auto trace = HHVM_FN(debug_backtrace)(opts);
319 auto tv = RuntimeOption::EvalHackArrDVArrs ?
320 make_tv<KindOfVec>(trace.detach()) :
321 make_tv<KindOfArray>(trace.detach());
322 cellMove(tv, trace_lval);
323 } else {
324 cellMove(
325 make_tv<KindOfResource>(createCompactBacktrace().detach()->hdr()),
326 trace_lval
330 VMRegAnchor _;
331 auto const fp = vmfp();
332 if (UNLIKELY(!fp)) return;
333 if (UNLIKELY(fp->func()->isBuiltin())) {
334 throwable_init_file_and_line_from_builtin(throwable);
335 } else {
336 auto const unit = fp->func()->unit();
337 auto const file = const_cast<StringData*>(unit->filepath());
338 auto const line = unit->getLineNumber(unit->offsetOf(vmpc()));
339 tvSetIgnoreRef(
340 make_tv<KindOfString>(file),
341 throwable->propLvalAtOffset(s_fileIdx)
343 tvSetIgnoreRef(
344 make_tv<KindOfInt64>(line),
345 throwable->propLvalAtOffset(s_lineIdx)
350 void throwable_init_file_and_line_from_builtin(ObjectData* throwable) {
351 assertx(vmfp_is_builtin());
352 throwable_init_file_and_line_from_trace(throwable);
355 void throwable_recompute_backtrace_from_wh(ObjectData* throwable,
356 c_WaitableWaitHandle* wh) {
357 assertx(is_throwable(throwable));
358 assertx(throwable_has_expected_props());
359 assertx(wh);
361 auto const trace_lval = throwable->propLvalAtOffset(s_traceIdx);
362 auto opts = exception_get_trace_options();
363 bool provide_object = opts & k_DEBUG_BACKTRACE_PROVIDE_OBJECT;
364 bool provide_metadata = opts & k_DEBUG_BACKTRACE_PROVIDE_METADATA;
365 bool ignore_args = opts & k_DEBUG_BACKTRACE_IGNORE_ARGS;
367 auto trace = createBacktrace(BacktraceArgs()
368 .fromWaitHandle(wh)
369 .withSelf()
370 .withThis(provide_object)
371 .withMetadata(provide_metadata)
372 .ignoreArgs(ignore_args));
373 auto tv = make_array_like_tv(trace.detach());
374 cellMove(tv, trace_lval);
375 throwable_init_file_and_line_from_trace(throwable);
378 String throwable_to_string(ObjectData* throwable) {
379 auto result = ObjectData::InvokeSimple(throwable, s___toString);
380 return result.isString()
381 ? result.toString()
382 : empty_string();
384 ///////////////////////////////////////////////////////////////////////////////