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 Slot s_fileIdx
{3};
35 const Slot s_lineIdx
{4};
36 const Slot s_traceIdx
{5};
37 const Slot s_traceOptsIdx
{0};
39 __thread
int tl_exit_code
{0};
41 ExtendedException::ExtendedException() : Exception() {
45 ExtendedException::ExtendedException(const std::string
&msg
) {
50 ExtendedException::ExtendedException(SkipFrame
, const std::string
&msg
) {
52 computeBacktrace(true);
55 ExtendedException::ExtendedException(const char *fmt
, ...) {
63 ExtendedException::ExtendedException(const std::string
& msg
,
71 * Normally we wouldn't need an explicit copy/move-constructors, or
72 * copy/move-assignment operators, but we have to make sure m_key isn't copied.
75 ExtendedException::ExtendedException(const ExtendedException
& other
)
78 m_silent(other
.m_silent
)
81 ExtendedException::ExtendedException(ExtendedException
&& other
) noexcept
82 : Exception(std::move(other
)),
83 m_btp(std::move(other
.m_btp
)),
84 m_silent(other
.m_silent
)
88 ExtendedException::operator=(const ExtendedException
& other
) {
89 Exception::operator=(other
);
91 m_silent
= other
.m_silent
;
96 ExtendedException::operator=(ExtendedException
&& other
) noexcept
{
97 Exception::operator=(std::move(other
));
98 m_btp
= std::move(other
.m_btp
);
99 m_silent
= other
.m_silent
;
103 Array
ExtendedException::getBacktrace() const {
104 return Array(m_btp
.get());
107 std::pair
<String
, int> ExtendedException::getFileAndLine() const {
108 String file
= empty_string();
110 Array bt
= getBacktrace();
112 Array top
= tvCastToArrayLike(bt
.rvalAt(0).tv());
113 if (top
.exists(s_file
)) file
= tvCastToString(top
.rvalAt(s_file
).tv());
114 if (top
.exists(s_line
)) line
= tvCastToInt64(top
.rvalAt(s_line
).tv());
116 return std::make_pair(file
, line
);
119 void ExtendedException::computeBacktrace(bool skipFrame
/* = false */) {
120 Array bt
= createBacktrace(BacktraceArgs()
127 //////////////////////////////////////////////////////////////////////
129 FatalErrorException::FatalErrorException(int, const char *msg
, ...) {
137 FatalErrorException::FatalErrorException(const std::string
& msg
,
138 const Array
& backtrace
,
139 bool isRecoverable
/* = false */)
140 : ExtendedException(msg
, backtrace
.get()), m_recoverable(isRecoverable
)
143 //////////////////////////////////////////////////////////////////////
145 void throw_null_pointer_exception() {
146 throw ExtendedException("A null object pointer was used.");
149 void throw_invalid_object_type(const char* clsName
) {
150 throw ExtendedException("Unexpected object type %s.", clsName
);
153 void throw_not_implemented(const char* feature
) {
154 throw ExtendedException("%s is not implemented yet.", feature
);
157 void throw_not_supported(const char* feature
, const char* reason
) {
158 throw ExtendedException("%s is not supported: %s", feature
, reason
);
161 ///////////////////////////////////////////////////////////////////////////////
164 void raise_fatal_error(const char* msg
,
165 const Array
& bt
/* = null_array */,
166 bool recoverable
/* = false */,
167 bool silent
/* = false */,
168 bool throws
/* = true */) {
169 if (RuntimeOption::PHP7_EngineExceptions
&& throws
) {
171 SystemLib::throwErrorObject(Variant(msg
));
173 auto ex
= bt
.isNull() && !recoverable
174 ? FatalErrorException(msg
)
175 : FatalErrorException(msg
, bt
, recoverable
);
176 ex
.setSilent(silent
);
180 ///////////////////////////////////////////////////////////////////////////////
183 DEBUG_ONLY
bool vmfp_is_builtin() {
185 auto const fp
= vmfp();
186 return fp
&& fp
->func()->isBuiltin();
189 DEBUG_ONLY
bool is_throwable(ObjectData
* throwable
) {
190 auto const erCls
= SystemLib::s_ErrorClass
;
191 auto const exCls
= SystemLib::s_ExceptionClass
;
192 return throwable
->instanceof(erCls
) || throwable
->instanceof(exCls
);
195 DEBUG_ONLY
bool throwable_has_expected_props() {
196 auto const erCls
= SystemLib::s_ErrorClass
;
197 auto const exCls
= SystemLib::s_ExceptionClass
;
199 erCls
->lookupDeclProp(s_file
.get()) == s_fileIdx
&&
200 exCls
->lookupDeclProp(s_file
.get()) == s_fileIdx
&&
201 erCls
->lookupDeclProp(s_line
.get()) == s_lineIdx
&&
202 exCls
->lookupDeclProp(s_line
.get()) == s_lineIdx
&&
203 erCls
->lookupDeclProp(s_trace
.get()) == s_traceIdx
&&
204 exCls
->lookupDeclProp(s_trace
.get()) == s_traceIdx
;
207 int64_t exception_get_trace_options() {
208 auto const exCls
= SystemLib::s_ExceptionClass
;
209 assertx(exCls
->lookupSProp(s_traceOpts
.get()) == s_traceOptsIdx
);
210 assertx(exCls
->needInitialization());
213 auto const traceOptsTV
= exCls
->getSPropData(s_traceOptsIdx
);
214 return traceOptsTV
->m_type
== KindOfInt64
215 ? traceOptsTV
->m_data
.num
: 0;
219 void throwable_init_file_and_line_from_builtin(ObjectData
* throwable
) {
220 assertx(vmfp_is_builtin());
221 assertx(is_throwable(throwable
));
222 assertx(throwable_has_expected_props());
224 auto const trace_rval
= throwable
->propRvalAtOffset(s_traceIdx
);
226 if (trace_rval
.type() == KindOfResource
) {
227 auto bt
= dyn_cast
<CompactTrace
>(Resource(trace_rval
.val().pres
));
230 for (auto& f
: bt
->frames()) {
231 if (!f
.func
|| f
.func
->isBuiltin()) continue;
233 auto const opAtPrevPc
= f
.func
->unit()->getOp(f
.prevPc
);
235 if (opAtPrevPc
== Op::PopR
||
236 opAtPrevPc
== Op::UnboxR
||
237 opAtPrevPc
== Op::UnboxRNop
) {
241 auto const ln
= f
.func
->unit()->getLineNumber(f
.prevPc
- pcAdjust
);
243 make_tv
<KindOfInt64
>(ln
),
244 throwable
->propLvalAtOffset(s_lineIdx
)
247 if (auto fn
= f
.func
->originalFilename()) {
249 make_tv
<KindOfPersistentString
>(fn
),
250 throwable
->propLvalAtOffset(s_fileIdx
)
254 make_tv
<KindOfPersistentString
>(f
.func
->unit()->filepath()),
255 throwable
->propLvalAtOffset(s_fileIdx
)
263 assertx(isArrayType(trace_rval
.type()));
264 auto const trace
= trace_rval
.val().parr
;
265 for (ArrayIter
iter(trace
); iter
; ++iter
) {
266 assertx(iter
.second().asTypedValue()->m_type
== KindOfArray
);
267 auto const frame
= iter
.second().asTypedValue()->m_data
.parr
;
268 auto const file
= frame
->rval(s_file
.get());
269 auto const line
= frame
->rval(s_line
.get());
272 auto const tv
= file
.tv();
275 throwable
->propLvalAtOffset(s_fileIdx
)
279 auto const tv
= line
.tv();
282 throwable
->propLvalAtOffset(s_lineIdx
)
290 void throwable_init(ObjectData
* throwable
) {
291 assertx(is_throwable(throwable
));
292 assertx(throwable_has_expected_props());
294 auto const trace_lval
= throwable
->propLvalAtOffset(s_traceIdx
);
295 auto opts
= exception_get_trace_options();
296 auto const filterOpts
= opts
& ~k_DEBUG_BACKTRACE_IGNORE_ARGS
;
298 !RuntimeOption::EvalEnableCompactBacktrace
|| filterOpts
||
299 (RuntimeOption::EnableArgsInBacktraces
&&
300 opts
!= k_DEBUG_BACKTRACE_IGNORE_ARGS
)
302 auto trace
= HHVM_FN(debug_backtrace
)(opts
);
303 cellMove(make_tv
<KindOfArray
>(trace
.detach()), trace_lval
);
306 make_tv
<KindOfResource
>(createCompactBacktrace().detach()->hdr()),
312 auto const fp
= vmfp();
313 if (UNLIKELY(!fp
)) return;
314 if (UNLIKELY(fp
->func()->isBuiltin())) {
315 throwable_init_file_and_line_from_builtin(throwable
);
317 auto const unit
= fp
->func()->unit();
318 auto const file
= const_cast<StringData
*>(unit
->filepath());
319 auto const line
= unit
->getLineNumber(unit
->offsetOf(vmpc()));
321 make_tv
<KindOfString
>(file
),
322 throwable
->propLvalAtOffset(s_fileIdx
)
325 make_tv
<KindOfInt64
>(line
),
326 throwable
->propLvalAtOffset(s_lineIdx
)
331 ///////////////////////////////////////////////////////////////////////////////