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/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"
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() {
46 ExtendedException::ExtendedException(const std::string
&msg
) {
51 ExtendedException::ExtendedException(SkipFrame
, const std::string
&msg
) {
53 computeBacktrace(true);
56 ExtendedException::ExtendedException(const char *fmt
, ...) {
64 ExtendedException::ExtendedException(const std::string
& 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
)
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
)
89 ExtendedException::operator=(const ExtendedException
& other
) {
90 Exception::operator=(other
);
92 m_silent
= other
.m_silent
;
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
;
104 Array
ExtendedException::getBacktrace() const {
105 return Array(m_btp
.get());
108 std::pair
<String
, int> ExtendedException::getFileAndLine() const {
109 String file
= empty_string();
111 Array bt
= getBacktrace();
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()
128 void ExtendedException::recomputeBacktraceFromWH(c_WaitableWaitHandle
* wh
) {
130 Array bt
= createBacktrace(BacktraceArgs()
137 //////////////////////////////////////////////////////////////////////
139 FatalErrorException::FatalErrorException(int, const char *msg
, ...) {
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 ///////////////////////////////////////////////////////////////////////////////
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
) {
181 SystemLib::throwErrorObject(Variant(msg
));
183 auto ex
= bt
.isNull() && !recoverable
184 ? FatalErrorException(msg
)
185 : FatalErrorException(msg
, bt
, recoverable
);
186 ex
.setSilent(silent
);
190 ///////////////////////////////////////////////////////////////////////////////
193 DEBUG_ONLY
bool vmfp_is_builtin() {
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
) {
216 // Check that we don't have the expected type-hints on these props so we
217 // don't need to verify anything.
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());
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
));
248 for (auto& f
: bt
->frames()) {
249 if (!f
.func
|| f
.func
->isBuiltin()) continue;
251 auto const ln
= f
.func
->unit()->getLineNumber(f
.prevPc
);
253 make_tv
<KindOfInt64
>(ln
),
254 throwable
->propLvalAtOffset(s_lineIdx
)
257 if (auto fn
= f
.func
->originalFilename()) {
259 make_tv
<KindOfPersistentString
>(fn
),
260 throwable
->propLvalAtOffset(s_fileIdx
)
264 make_tv
<KindOfPersistentString
>(f
.func
->unit()->filepath()),
265 throwable
->propLvalAtOffset(s_fileIdx
)
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());
287 auto const tv
= file
.tv();
290 throwable
->propLvalAtOffset(s_fileIdx
)
294 auto const tv
= line
.tv();
297 throwable
->propLvalAtOffset(s_lineIdx
)
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
;
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
);
325 make_tv
<KindOfResource
>(createCompactBacktrace().detach()->hdr()),
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
);
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()));
340 make_tv
<KindOfString
>(file
),
341 throwable
->propLvalAtOffset(s_fileIdx
)
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());
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()
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()
384 ///////////////////////////////////////////////////////////////////////////////