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/execution-context.h"
18 #define __STDC_LIMIT_MACROS
25 #include <folly/MapUtil.h>
26 #include <folly/Format.h>
27 #include <folly/Likely.h>
29 #include "hphp/util/logger.h"
30 #include "hphp/util/process.h"
31 #include "hphp/util/text-color.h"
32 #include "hphp/util/service-data.h"
33 #include "hphp/runtime/base/request-event-handler.h"
34 #include "hphp/runtime/base/array-init.h"
35 #include "hphp/runtime/base/debuggable.h"
36 #include "hphp/runtime/base/array-iterator.h"
37 #include "hphp/runtime/base/apc-gc-manager.h"
38 #include "hphp/runtime/base/memory-manager.h"
39 #include "hphp/runtime/base/sweepable.h"
40 #include "hphp/runtime/base/builtin-functions.h"
41 #include "hphp/runtime/base/comparisons.h"
42 #include "hphp/runtime/base/externals.h"
43 #include "hphp/runtime/base/program-functions.h"
44 #include "hphp/runtime/base/runtime-option.h"
45 #include "hphp/runtime/base/tv-refcount.h"
46 #include "hphp/runtime/base/tv-type.h"
47 #include "hphp/runtime/base/unit-cache.h"
48 #include "hphp/runtime/base/system-profiler.h"
49 #include "hphp/runtime/base/container-functions.h"
50 #include "hphp/runtime/base/hhprof.h"
51 #include "hphp/runtime/base/apc-stats.h"
52 #include "hphp/runtime/base/apc-typed-value.h"
53 #include "hphp/runtime/base/extended-logger.h"
54 #include "hphp/runtime/base/zend-math.h"
55 #include "hphp/runtime/debugger/debugger.h"
56 #include "hphp/runtime/ext/std/ext_std_output.h"
57 #include "hphp/runtime/ext/string/ext_string.h"
58 #include "hphp/runtime/ext/reflection/ext_reflection.h"
59 #include "hphp/runtime/ext/apc/ext_apc.h"
60 #include "hphp/runtime/server/cli-server.h"
61 #include "hphp/runtime/server/server-stats.h"
62 #include "hphp/runtime/vm/debug/debug.h"
63 #include "hphp/runtime/vm/jit/enter-tc.h"
64 #include "hphp/runtime/vm/jit/tc.h"
65 #include "hphp/runtime/vm/jit/translator-inline.h"
66 #include "hphp/runtime/vm/jit/translator.h"
67 #include "hphp/runtime/vm/act-rec-defs.h"
68 #include "hphp/runtime/vm/debugger-hook.h"
69 #include "hphp/runtime/vm/event-hook.h"
70 #include "hphp/runtime/vm/hh-utils.h"
71 #include "hphp/runtime/vm/interp-helpers.h"
72 #include "hphp/runtime/vm/runtime.h"
73 #include "hphp/runtime/vm/runtime-compiler.h"
74 #include "hphp/runtime/vm/treadmill.h"
75 #include "hphp/runtime/vm/unwind.h"
76 #include "hphp/runtime/base/php-globals.h"
77 #include "hphp/runtime/base/exceptions.h"
80 ///////////////////////////////////////////////////////////////////////////////
81 TRACE_SET_MOD(bcinterp
);
83 rds::local::AliasedRDSLocal
<ExecutionContext
,
84 rds::local::Initialize::Explicitly
,
85 &rds::local::detail::HotRDSLocals::g_context
88 ExecutionContext::ExecutionContext()
89 : m_transport(nullptr)
91 , m_implicitFlush(false)
93 , m_stdoutBytesWritten(0)
94 , m_errorState(ExecutionContext::ErrorState::NoError
)
96 , m_deferredErrors(staticEmptyVecArray())
97 , m_throwAllErrors(false)
98 , m_softLateInitDefault
{init_null_variant
}
99 , m_pageletTasksStarted(0)
101 , m_globalVarEnv(nullptr)
104 , m_dbgNoBreak(false)
105 , m_unwindingCppException(false)
106 , m_lastErrorPath(staticEmptyString())
108 , m_executingSetprofileCallback(false)
109 , m_logger_hook(*this)
111 resetCoverageCounters();
112 // We don't want a new execution context to cause any request-heap
113 // allocations (because it will cause us to hold a slab, even while idle).
114 static auto s_cwd
= makeStaticString(Process::CurrentWorkingDirectory
);
116 RID().setMemoryLimit(std::to_string(RuntimeOption::RequestMemoryMaxBytes
));
117 RID().setErrorReportingLevel(RuntimeOption::RuntimeErrorReportingLevel
);
119 VariableSerializer::serializationSizeLimit
->value
=
120 RuntimeOption::SerializationSizeLimit
;
121 tvWriteUninit(m_headerCallback
);
123 m_shutdowns
[ShutdownType::ShutDown
] = empty_vec_array();
124 m_shutdowns
[ShutdownType::PostSend
] = empty_vec_array();
125 m_shutdownsBackup
[ShutdownType::ShutDown
] = empty_vec_array();
126 m_shutdownsBackup
[ShutdownType::PostSend
] = empty_vec_array();
129 namespace rds
{ namespace local
{
130 // See header for why this is required.
134 void rds::local::RDSLocal
<ExecutionContext
,
135 rds::local::Initialize::Explicitly
>::destroy() {
137 getNoCheck()->sweep();
144 void ExecutionContext::cleanup() {
148 void ExecutionContext::sweep() {
152 ExecutionContext::~ExecutionContext() {
156 void ExecutionContext::backupSession() {
157 m_shutdownsBackup
= m_shutdowns
;
158 m_userErrorHandlersBackup
= m_userErrorHandlers
;
159 m_userExceptionHandlersBackup
= m_userExceptionHandlers
;
162 void ExecutionContext::restoreSession() {
163 m_shutdowns
= m_shutdownsBackup
;
164 m_userErrorHandlers
= m_userErrorHandlersBackup
;
165 m_userExceptionHandlers
= m_userExceptionHandlersBackup
;
168 ///////////////////////////////////////////////////////////////////////////////
171 String
ExecutionContext::getMimeType() const {
174 mimetype
= m_transport
->getMimeType();
177 if (strncasecmp(mimetype
.data(), "text/", 5) == 0) {
178 int pos
= mimetype
.find(';');
179 if (pos
!= String::npos
) {
180 mimetype
= mimetype
.substr(0, pos
);
182 } else if (m_transport
&& m_transport
->getUseDefaultContentType()) {
183 mimetype
= RID().getDefaultMimeType();
188 std::string
ExecutionContext::getRequestUrl(size_t szLimit
) {
189 Transport
* t
= getTransport();
190 std::string ret
= t
? t
->getUrl() : "";
191 if (szLimit
!= std::string::npos
) {
192 ret
= ret
.substr(0, szLimit
);
197 void ExecutionContext::setContentType(const String
& mimetype
,
198 const String
& charset
) {
200 String contentType
= mimetype
;
202 contentType
+= "charset=";
203 contentType
+= charset
;
204 m_transport
->addHeader("Content-Type", contentType
.c_str());
205 m_transport
->setUseDefaultContentType(false);
209 ///////////////////////////////////////////////////////////////////////////////
212 void ExecutionContext::write(const String
& s
) {
213 write(s
.data(), s
.size());
216 void ExecutionContext::addStdoutHook(StdoutHook
* hook
) {
217 if (hook
!= nullptr) {
218 m_stdoutHooks
.insert(hook
);
222 bool ExecutionContext::removeStdoutHook(StdoutHook
* hook
) {
223 if (hook
== nullptr) {
227 return m_stdoutHooks
.erase(hook
) != 0;
230 static void safe_stdout(const void *ptr
, size_t size
) {
231 write(fileno(stdout
), ptr
, size
);
234 void ExecutionContext::writeStdout(const char *s
, int len
) {
236 if (m_stdoutHooks
.empty()) {
237 if (s_stdout_color
) {
238 safe_stdout(s_stdout_color
, strlen(s_stdout_color
));
240 safe_stdout(ANSI_COLOR_END
, strlen(ANSI_COLOR_END
));
244 m_stdoutBytesWritten
+= len
;
246 for (auto const hook
: m_stdoutHooks
) {
247 assertx(hook
!= nullptr);
253 void ExecutionContext::writeTransport(const char *s
, int len
) {
255 m_transport
->sendRaw(s
, len
, 200, false, true);
261 size_t ExecutionContext::getStdoutBytesWritten() const {
262 return m_stdoutBytesWritten
;
265 void ExecutionContext::write(const char *s
, int len
) {
267 m_sb
->append(s
, len
);
268 if (m_out
&& m_out
->chunk_size
> 0) {
269 if (m_sb
->size() >= m_out
->chunk_size
) {
273 if (m_implicitFlush
) flush();
275 writeTransport(s
, len
);
279 ///////////////////////////////////////////////////////////////////////////////
282 void ExecutionContext::obProtect(bool on
) {
283 m_protectedLevel
= on
? m_buffers
.size() : 0;
286 void ExecutionContext::obStart(const Variant
& handler
/* = null */,
287 int chunk_size
/* = 0 */,
288 OBFlags flags
/* = OBFlags::Default */) {
289 if (m_insideOBHandler
) {
290 raise_error("ob_start(): Cannot use output buffering "
291 "in output buffering display handlers");
293 m_buffers
.emplace_back(Variant(handler
), chunk_size
, flags
);
294 resetCurrentBuffer();
297 String
ExecutionContext::obCopyContents() {
298 if (!m_buffers
.empty()) {
299 StringBuffer
&oss
= m_buffers
.back().oss
;
304 return empty_string();
307 String
ExecutionContext::obDetachContents() {
308 if (!m_buffers
.empty()) {
309 StringBuffer
&oss
= m_buffers
.back().oss
;
314 return empty_string();
317 int ExecutionContext::obGetContentLength() {
318 if (m_buffers
.empty()) {
321 return m_buffers
.back().oss
.size();
324 void ExecutionContext::obClean(int handler_flag
) {
325 if (!m_buffers
.empty()) {
326 OutputBuffer
*last
= &m_buffers
.back();
327 if (!last
->handler
.isNull()) {
328 m_insideOBHandler
= true;
329 SCOPE_EXIT
{ m_insideOBHandler
= false; };
330 vm_call_user_func(last
->handler
,
331 make_vec_array(last
->oss
.detach(), handler_flag
));
337 bool ExecutionContext::obFlush(bool force
/*= false*/) {
338 assertx(m_protectedLevel
>= 0);
340 if ((int)m_buffers
.size() <= m_protectedLevel
) {
344 auto iter
= m_buffers
.end();
345 OutputBuffer
& last
= *(--iter
);
346 if (!force
&& !(last
.flags
& OBFlags::Flushable
)) {
349 if (any(last
.flags
& OBFlags::OutputDisabled
)) {
353 const int flag
= k_PHP_OUTPUT_HANDLER_START
| k_PHP_OUTPUT_HANDLER_END
;
355 if (iter
!= m_buffers
.begin()) {
356 OutputBuffer
& prev
= *(--iter
);
357 if (last
.handler
.isNull()) {
358 prev
.oss
.absorb(last
.oss
);
360 auto str
= last
.oss
.detach();
364 m_insideOBHandler
= true;
365 SCOPE_EXIT
{ m_insideOBHandler
= false; };
366 tout
= vm_call_user_func(
367 last
.handler
, make_vec_array(str
, flag
)
370 prev
.oss
.append(tout
.toString());
372 prev
.oss
.append(str
);
379 auto str
= last
.oss
.detach();
380 if (!last
.handler
.isNull()) {
384 m_insideOBHandler
= true;
385 SCOPE_EXIT
{ m_insideOBHandler
= false; };
386 tout
= vm_call_user_func(
387 last
.handler
, make_vec_array(str
, flag
)
390 str
= tout
.toString();
392 writeTransport(str
.data(), str
.size());
397 writeTransport(str
.data(), str
.size());
401 void ExecutionContext::obFlushAll() {
407 bool ExecutionContext::obEnd() {
408 assertx(m_protectedLevel
>= 0);
409 if ((int)m_buffers
.size() > m_protectedLevel
) {
410 m_buffers
.pop_back();
411 resetCurrentBuffer();
412 if (m_implicitFlush
) flush();
415 if (m_implicitFlush
) flush();
419 void ExecutionContext::obEndAll() {
423 int ExecutionContext::obGetLevel() {
424 assertx((int)m_buffers
.size() >= m_protectedLevel
);
425 return m_buffers
.size() - m_protectedLevel
;
434 s_chunk_size("chunk_size"),
435 s_buffer_used("buffer_used"),
436 s_default_output_handler("default output handler");
438 Array
ExecutionContext::obGetStatus(bool full
) {
439 Array ret
= Array::CreateVArray();
441 for (auto& buffer
: m_buffers
) {
442 Array status
= Array::CreateDArray();
443 if (level
< m_protectedLevel
|| buffer
.handler
.isNull()) {
444 status
.set(s_name
, s_default_output_handler
);
445 status
.set(s_type
, 0);
447 status
.set(s_name
, buffer
.handler
);
448 status
.set(s_type
, 1);
452 if (any(buffer
.flags
& OBFlags::Cleanable
)) {
453 flags
|= k_PHP_OUTPUT_HANDLER_CLEANABLE
;
455 if (any(buffer
.flags
& OBFlags::Flushable
)) {
456 flags
|= k_PHP_OUTPUT_HANDLER_FLUSHABLE
;
458 if (any(buffer
.flags
& OBFlags::Removable
)) {
459 flags
|= k_PHP_OUTPUT_HANDLER_REMOVABLE
;
461 status
.set(s_flags
, flags
);
463 status
.set(s_level
, level
);
464 status
.set(s_chunk_size
, buffer
.chunk_size
);
465 status
.set(s_buffer_used
, static_cast<uint64_t>(buffer
.oss
.size()));
470 ret
= std::move(status
);
477 String
ExecutionContext::obGetBufferName() {
478 if (m_buffers
.empty()) {
480 } else if (m_buffers
.size() <= m_protectedLevel
) {
481 return s_default_output_handler
;
483 auto iter
= m_buffers
.end();
484 OutputBuffer
& buffer
= *(--iter
);
485 if (buffer
.handler
.isNull()) {
486 return s_default_output_handler
;
488 return buffer
.handler
.toString();
493 void ExecutionContext::obSetImplicitFlush(bool on
) {
494 m_implicitFlush
= on
;
497 Array
ExecutionContext::obGetHandlers() {
498 Array ret
= Array::CreateVArray();
499 for (auto& ob
: m_buffers
) {
500 auto& handler
= ob
.handler
;
501 ret
.append(handler
.isNull() ? s_default_output_handler
: handler
);
506 void ExecutionContext::flush() {
507 if (!m_buffers
.empty() &&
508 RuntimeOption::EnableEarlyFlush
&& m_protectedLevel
&&
509 !(m_buffers
.front().flags
& OBFlags::OutputDisabled
)) {
510 OutputBuffer
&buffer
= m_buffers
.front();
511 StringBuffer
&oss
= buffer
.oss
;
513 if (any(buffer
.flags
& OBFlags::WriteToStdout
)) {
514 writeStdout(oss
.data(), oss
.size());
516 writeTransport(oss
.data(), oss
.size());
523 void ExecutionContext::resetCurrentBuffer() {
524 if (m_buffers
.empty()) {
528 m_sb
= &m_buffers
.back().oss
;
529 m_out
= &m_buffers
.back();
533 ///////////////////////////////////////////////////////////////////////////////
534 // program executions
536 void ExecutionContext::registerShutdownFunction(const Variant
& function
,
539 auto& funcs
= m_shutdowns
[type
];
540 assertx(funcs
.isVecArray());
541 funcs
.append(make_dict_array(
548 bool ExecutionContext::removeShutdownFunction(const Variant
& function
,
551 auto const& funcs
= m_shutdowns
[type
];
552 assertx(funcs
.isVecArray());
553 VecArrayInit
newFuncs(funcs
.size());
558 auto const& arr
= asCArrRef(&v
);
559 assertx(arr
->isDict());
560 if (!same(arr
[s_name
], function
)) {
567 m_shutdowns
[type
] = newFuncs
.toArray();
571 Variant
ExecutionContext::pushUserErrorHandler(const Variant
& function
,
574 if (!m_userErrorHandlers
.empty()) {
575 ret
= m_userErrorHandlers
.back().first
;
577 m_userErrorHandlers
.push_back(std::pair
<Variant
,int>(function
, error_types
));
581 Variant
ExecutionContext::pushUserExceptionHandler(const Variant
& function
) {
583 if (!m_userExceptionHandlers
.empty()) {
584 ret
= m_userExceptionHandlers
.back();
586 m_userExceptionHandlers
.push_back(function
);
590 void ExecutionContext::popUserErrorHandler() {
591 if (!m_userErrorHandlers
.empty()) {
592 m_userErrorHandlers
.pop_back();
596 void ExecutionContext::clearUserErrorHandlers() {
597 while (!m_userErrorHandlers
.empty()) m_userErrorHandlers
.pop_back();
600 void ExecutionContext::popUserExceptionHandler() {
601 if (!m_userExceptionHandlers
.empty()) {
602 m_userExceptionHandlers
.pop_back();
606 void ExecutionContext::acceptRequestEventHandlers(bool enable
) {
607 m_acceptRequestEventHandlers
= enable
;
610 std::size_t ExecutionContext::registerRequestEventHandler(
611 RequestEventHandler
*handler
) {
612 assertx(handler
&& handler
->getInited());
613 assertx(m_acceptRequestEventHandlers
);
614 m_requestEventHandlers
.push_back(handler
);
615 return m_requestEventHandlers
.size()-1;
618 void ExecutionContext::unregisterRequestEventHandler(
619 RequestEventHandler
* handler
,
621 assertx(index
< m_requestEventHandlers
.size() &&
622 m_requestEventHandlers
[index
] == handler
);
623 assertx(!handler
->getInited());
624 if (index
== m_requestEventHandlers
.size()-1) {
625 m_requestEventHandlers
.pop_back();
627 m_requestEventHandlers
[index
] = nullptr;
631 static bool requestEventHandlerPriorityComp(RequestEventHandler
*a
,
632 RequestEventHandler
*b
) {
634 else if (!b
) return false;
635 else return a
->priority() < b
->priority();
638 void ExecutionContext::onRequestShutdown() {
639 while (!m_requestEventHandlers
.empty()) {
640 // handlers could cause other handlers to be registered,
641 // so need to repeat until done
642 decltype(m_requestEventHandlers
) tmp
;
643 tmp
.swap(m_requestEventHandlers
);
645 // Sort handlers by priority so that lower priority values get shutdown
647 sort(tmp
.begin(), tmp
.end(),
648 requestEventHandlerPriorityComp
);
649 for (auto* handler
: tmp
) {
650 if (!handler
) continue;
651 assertx(handler
->getInited());
652 handler
->requestShutdown();
653 handler
->setInited(false);
658 void ExecutionContext::executeFunctions(ShutdownType type
) {
659 RID().resetTimer(RuntimeOption::PspTimeoutSeconds
);
660 RID().resetCPUTimer(RuntimeOption::PspCpuTimeoutSeconds
);
662 // We mustn't destroy any callbacks until we're done with all
663 // of them. So hold them in tmp.
664 // XXX still true in a world without destructors?
665 auto tmp
= empty_vec_array();
667 Array funcs
= m_shutdowns
[type
];
668 if (funcs
.empty()) break;
669 m_shutdowns
[type
] = empty_vec_array();
673 auto const& cb
= asCArrRef(&v
);
674 assertx(cb
->isDict());
675 vm_call_user_func(cb
[s_name
], cb
[s_args
].toArray());
682 void ExecutionContext::onShutdownPreSend() {
683 // in case obStart was called without obFlush
685 try { obFlushAll(); } catch (...) {}
688 // When host is OOMing, abort abruptly.
689 if (RID().shouldOOMAbort()) return;
691 tl_heap
->resetCouldOOM(isStandardRequest());
692 executeFunctions(ShutDown
);
695 extern void ext_session_request_shutdown();
697 void ExecutionContext::debuggerExecutePsps() {
699 executeFunctions(PostSend
);
700 } catch (const ExitException
& e
) {
702 } catch (const Exception
& e
) {
704 } catch (const Object
& e
) {
705 onUnhandledException(e
);
710 void ExecutionContext::onShutdownPostSend() {
711 // When host is OOMing, abort abruptly.
712 if (RID().shouldOOMAbort()) return;
714 ServerStats::SetThreadMode(ServerStats::ThreadMode::PostProcessing
);
715 tl_heap
->resetCouldOOM(isStandardRequest());
718 ServerStatsHelper
ssh("psp", ServerStatsHelper::TRACK_HWINST
);
719 executeFunctions(PostSend
);
722 bump_counter_and_rethrow(true /* isPsp */);
723 } catch (const ExitException
& e
) {
725 } catch (const Exception
& e
) {
727 } catch (const Object
& e
) {
728 onUnhandledException(e
);
732 Logger::Error("unknown exception was thrown from psp");
736 * This has to happen before requestEventHandler shutdown hooks,
737 * because it can run user code which may need to access other
738 * RequestLocal objects (such as the stream registry).
740 ext_session_request_shutdown();
742 ServerStats::SetThreadMode(ServerStats::ThreadMode::Idling
);
745 ///////////////////////////////////////////////////////////////////////////////
748 bool ExecutionContext::errorNeedsHandling(int errnum
,
749 bool callUserHandler
,
750 ErrorThrowMode mode
) {
751 if (UNLIKELY(m_throwAllErrors
)) {
752 throw Exception(folly::sformat("throwAllErrors: {}", errnum
));
754 if (mode
!= ErrorThrowMode::Never
|| errorNeedsLogging(errnum
)) {
757 if (callUserHandler
) {
758 if (!m_userErrorHandlers
.empty() &&
759 (m_userErrorHandlers
.back().second
& errnum
) != 0) {
766 bool ExecutionContext::errorNeedsLogging(int errnum
) {
768 RID().getErrorReportingLevel() |
769 RuntimeOption::ForceErrorReportingLevel
;
770 return RuntimeOption::NoSilencer
|| (level
& errnum
) != 0;
773 struct ErrorStateHelper
{
774 ErrorStateHelper(ExecutionContext
*context
,
775 ExecutionContext::ErrorState state
) {
777 m_originalState
= m_context
->getErrorState();
778 m_context
->setErrorState(state
);
780 ~ErrorStateHelper() {
781 m_context
->setErrorState(m_originalState
);
784 ExecutionContext
*m_context
;
785 ExecutionContext::ErrorState m_originalState
;
791 s_function("function"),
793 s_error_num("error-num"),
794 s_error_string("error-string"),
795 s_error_file("error-file"),
796 s_error_line("error-line"),
797 s_error_backtrace("error-backtrace"),
798 s_overflow("overflow");
800 void ExecutionContext::handleError(const std::string
& msg
,
802 bool callUserHandler
,
804 const std::string
& prefix
,
805 bool skipFrame
/* = false */) {
806 SYNC_VM_REGS_SCOPED();
808 auto newErrorState
= ErrorState::ErrorRaised
;
809 switch (getErrorState()) {
810 case ErrorState::ErrorRaised
:
811 case ErrorState::ErrorRaisedByUserHandler
:
813 case ErrorState::ExecutingUserHandler
:
814 newErrorState
= ErrorState::ErrorRaisedByUserHandler
;
820 // Potentially upgrade the error to E_USER_ERROR
821 if (errnum
& RuntimeOption::ErrorUpgradeLevel
&
822 static_cast<int>(ErrorMode::UPGRADEABLE_ERROR
)) {
823 errnum
= static_cast<int>(ErrorMode::USER_ERROR
);
824 mode
= ErrorThrowMode::IfUnhandled
;
827 auto const ee
= skipFrame
?
828 ExtendedException(ExtendedException::SkipFrame
{}, msg
) :
829 ExtendedException(msg
);
830 bool handled
= false;
832 ErrorStateHelper
esh(this, newErrorState
);
833 if (callUserHandler
) {
835 RuntimeOption::EvalDisableErrorHandler
||
836 callUserErrorHandler(ee
, errnum
, false);
840 recordLastError(ee
, errnum
);
843 if (g_system_profiler
) {
844 g_system_profiler
->errorCallBack(ee
, errnum
, msg
);
848 if (mode
== ErrorThrowMode::Always
||
849 (mode
== ErrorThrowMode::IfUnhandled
&& !handled
)) {
850 DEBUGGER_ATTACHED_ONLY(phpDebuggerErrorHook(ee
, errnum
, msg
));
852 errnum
== static_cast<int>(ErrorMode::RECOVERABLE_ERROR
);
853 raise_fatal_error(msg
.c_str(), ee
.getBacktrace(), isRecoverable
,
854 !errorNeedsLogging(errnum
) /* silent */);
861 // If we're inside an error handler already, queue it up on the deferred
863 if (getErrorState() == ErrorState::ExecutingUserHandler
) {
864 auto& deferred
= m_deferredErrors
;
865 if (deferred
.size() < RuntimeOption::EvalMaxDeferredErrors
) {
866 auto fileAndLine
= ee
.getFileAndLine();
871 s_error_file
, std::move(fileAndLine
.first
),
872 s_error_line
, fileAndLine
.second
,
873 s_error_backtrace
, ee
.getBacktrace()
876 } else if (!deferred
.empty()) {
877 auto const last
= deferred
.lvalAt(int64_t{deferred
.size() - 1});
878 if (isDictType(type(last
))) {
879 asArrRef(last
).set(s_overflow
, true);
884 if (errorNeedsLogging(errnum
)) {
885 DEBUGGER_ATTACHED_ONLY(phpDebuggerErrorHook(ee
, errnum
, ee
.getMessage()));
886 auto fileAndLine
= ee
.getFileAndLine();
887 Logger::Log(Logger::LogError
, prefix
.c_str(), ee
,
888 fileAndLine
.first
.c_str(), fileAndLine
.second
);
893 bool ExecutionContext::callUserErrorHandler(const Exception
& e
, int errnum
,
894 bool swallowExceptions
) {
895 switch (getErrorState()) {
896 case ErrorState::ExecutingUserHandler
:
897 case ErrorState::ErrorRaisedByUserHandler
:
902 if (!m_userErrorHandlers
.empty() &&
903 (m_userErrorHandlers
.back().second
& errnum
) != 0) {
904 auto fileAndLine
= std::make_pair(empty_string(), 0);
906 if (auto const ee
= dynamic_cast<const ExtendedException
*>(&e
)) {
907 fileAndLine
= ee
->getFileAndLine();
908 backtrace
= ee
->getBacktrace();
911 ErrorStateHelper
esh(this, ErrorState::ExecutingUserHandler
);
912 m_deferredErrors
= Array::CreateVec();
913 SCOPE_EXIT
{ m_deferredErrors
= Array::CreateVec(); };
914 if (!same(vm_call_user_func
915 (m_userErrorHandlers
.back().first
,
916 make_vec_array(errnum
, String(e
.getMessage()),
917 fileAndLine
.first
, fileAndLine
.second
, empty_darray(),
922 } catch (const RequestTimeoutException
&) {
923 static auto requestErrorHandlerTimeoutCounter
=
924 ServiceData::createTimeSeries("requests_timed_out_error_handler",
925 {ServiceData::StatsType::COUNT
});
926 requestErrorHandlerTimeoutCounter
->addValue(1);
927 ServerStats::Log("request.timed_out.error_handler", 1);
929 if (!swallowExceptions
) throw;
930 } catch (const RequestCPUTimeoutException
&) {
931 static auto requestErrorHandlerCPUTimeoutCounter
=
932 ServiceData::createTimeSeries("requests_cpu_timed_out_error_handler",
933 {ServiceData::StatsType::COUNT
});
934 requestErrorHandlerCPUTimeoutCounter
->addValue(1);
935 ServerStats::Log("request.cpu_timed_out.error_handler", 1);
937 if (!swallowExceptions
) throw;
938 } catch (const RequestMemoryExceededException
&) {
939 static auto requestErrorHandlerMemoryExceededCounter
=
940 ServiceData::createTimeSeries(
941 "requests_memory_exceeded_error_handler",
942 {ServiceData::StatsType::COUNT
});
943 requestErrorHandlerMemoryExceededCounter
->addValue(1);
944 ServerStats::Log("request.memory_exceeded.error_handler", 1);
946 if (!swallowExceptions
) throw;
948 static auto requestErrorHandlerOtherExceptionCounter
=
949 ServiceData::createTimeSeries(
950 "requests_other_exception_error_handler",
951 {ServiceData::StatsType::COUNT
});
952 requestErrorHandlerOtherExceptionCounter
->addValue(1);
953 ServerStats::Log("request.other_exception.error_handler", 1);
955 if (!swallowExceptions
) throw;
961 bool ExecutionContext::onFatalError(const Exception
& e
) {
962 tl_heap
->resetCouldOOM(isStandardRequest());
964 // need to restore the error reporting level, because the fault
965 // handler for silencers won't be run on fatals, and we might be
966 // about to run a user error handler (and psp/shutdown code).
967 RID().setErrorReportingLevel(RuntimeOption::RuntimeErrorReportingLevel
);
969 auto prefix
= "\nFatal error: ";
970 auto errnum
= static_cast<int>(ErrorMode::FATAL_ERROR
);
971 auto const fatal
= dynamic_cast<const FatalErrorException
*>(&e
);
972 if (fatal
&& fatal
->isRecoverable()) {
973 prefix
= "\nCatchable fatal error: ";
974 errnum
= static_cast<int>(ErrorMode::RECOVERABLE_ERROR
);
977 recordLastError(e
, errnum
);
979 bool silenced
= false;
980 auto fileAndLine
= std::make_pair(empty_string(), 0);
981 if (auto const ee
= dynamic_cast<const ExtendedException
*>(&e
)) {
982 silenced
= ee
->isSilent();
983 fileAndLine
= ee
->getFileAndLine();
985 // need to silence even with the AlwaysLogUnhandledExceptions flag set
986 if (!silenced
&& RuntimeOption::AlwaysLogUnhandledExceptions
) {
987 Logger::Log(Logger::LogError
, prefix
, e
, fileAndLine
.first
.c_str(),
990 bool handled
= false;
991 if (RuntimeOption::CallUserHandlerOnFatals
) {
993 RuntimeOption::EvalDisableErrorHandler
||
994 callUserErrorHandler(e
, errnum
, true);
996 if (!handled
&& !silenced
&& !RuntimeOption::AlwaysLogUnhandledExceptions
) {
997 Logger::Log(Logger::LogError
, prefix
, e
, fileAndLine
.first
.c_str(),
1003 bool ExecutionContext::onUnhandledException(Object e
) {
1004 String err
= throwable_to_string(e
.get());
1005 if (RuntimeOption::AlwaysLogUnhandledExceptions
) {
1006 Logger::Error("\nFatal error: Uncaught %s", err
.data());
1009 if (e
.instanceof(SystemLib::s_ThrowableClass
)) {
1010 // user thrown exception
1011 if (!m_userExceptionHandlers
.empty()) {
1012 if (!same(vm_call_user_func
1013 (m_userExceptionHandlers
.back(),
1024 if (!RuntimeOption::AlwaysLogUnhandledExceptions
) {
1025 Logger::Error("\nFatal error: Uncaught %s", err
.data());
1030 ///////////////////////////////////////////////////////////////////////////////
1032 void ExecutionContext::debuggerInfo(
1033 std::vector
<std::pair
<const char*,std::string
>>& info
) {
1034 int64_t newInt
= convert_bytes_to_long(IniSetting::Get("memory_limit"));
1036 newInt
= std::numeric_limits
<int64_t>::max();
1038 if (newInt
== std::numeric_limits
<int64_t>::max()) {
1039 info
.emplace_back("Max Memory", "(unlimited)");
1041 info
.emplace_back("Max Memory", IDebuggable::FormatSize(newInt
));
1043 info
.emplace_back("Max Time",
1044 IDebuggable::FormatTime(RID().getTimeout() * 1000));
1047 void ExecutionContext::setenv(const String
& name
, const String
& value
) {
1048 m_envs
.set(m_envs
.convertKey
<IntishCast::Cast
>(name
),
1049 make_tv
<KindOfString
>(value
.get()));
1052 void ExecutionContext::unsetenv(const String
& name
) {
1053 m_envs
.remove(name
);
1056 String
ExecutionContext::getenv(const String
& name
) const {
1057 if (m_envs
.exists(name
)) {
1058 return m_envs
[name
].toString();
1060 if (is_cli_mode()) {
1061 auto envs
= cli_env();
1062 if (envs
.exists(name
)) return envs
[name
].toString();
1065 if (auto value
= ::getenv(name
.data())) {
1066 return String(value
, CopyString
);
1068 if (RuntimeOption::EnvVariables
.find(name
.c_str()) != RuntimeOption::EnvVariables
.end()) {
1069 return String(RuntimeOption::EnvVariables
[name
.c_str()].data(), CopyString
);
1074 Cell
ExecutionContext::lookupClsCns(const NamedEntity
* ne
,
1075 const StringData
* cls
,
1076 const StringData
* cns
) {
1077 Class
* class_
= nullptr;
1079 class_
= Unit::loadClass(ne
, cls
);
1080 } catch (Object
& ex
) {
1081 // For compatibility with php, throwing through a constant lookup has
1082 // different behavior inside a property initializer (86pinit/86sinit).
1083 auto ar
= getStackFrame();
1084 if (ar
&& ar
->func() && Func::isSpecial(ar
->func()->name())) {
1085 raise_warning("Uncaught %s", ex
.toString().data());
1086 raise_error("Couldn't find constant %s::%s", cls
->data(), cns
->data());
1090 if (class_
== nullptr) {
1091 raise_error(Strings::UNKNOWN_CLASS
, cls
->data());
1093 Cell clsCns
= class_
->clsCnsGet(cns
);
1094 if (clsCns
.m_type
== KindOfUninit
) {
1095 raise_error("Couldn't find constant %s::%s", cls
->data(), cns
->data());
1100 static Class
* loadClass(StringData
* clsName
) {
1101 Class
* class_
= Unit::loadClass(clsName
);
1102 if (class_
== nullptr) {
1103 raise_error(Strings::UNKNOWN_CLASS
, clsName
->data());
1108 ObjectData
* ExecutionContext::createObject(StringData
* clsName
,
1109 const Variant
& params
,
1110 bool init
/* = true */) {
1111 return createObject(loadClass(clsName
), params
, init
);
1114 ObjectData
* ExecutionContext::createObject(const Class
* class_
,
1115 const Variant
& params
,
1117 callerDynamicConstructChecks(class_
);
1118 auto o
= Object::attach(newInstance(const_cast<Class
*>(class_
)));
1120 initObject(class_
, params
, o
.get());
1126 ObjectData
* ExecutionContext::createObjectOnly(StringData
* clsName
) {
1127 return createObject(clsName
, init_null_variant
, false);
1130 ObjectData
* ExecutionContext::initObject(StringData
* clsName
,
1131 const Variant
& params
,
1133 return initObject(loadClass(clsName
), params
, o
);
1136 ObjectData
* ExecutionContext::initObject(const Class
* class_
,
1137 const Variant
& params
,
1139 auto ctor
= class_
->getCtor();
1140 if (!(ctor
->attrs() & AttrPublic
)) {
1141 std::string msg
= "Access to non-public constructor of class ";
1142 msg
+= class_
->name()->data();
1143 Reflection::ThrowReflectionExceptionObject(msg
);
1146 if (!isContainerOrNull(params
)) {
1147 throw_param_is_not_container();
1149 tvDecRefGen(invokeFunc(ctor
, params
, o
));
1153 ActRec
* ExecutionContext::getStackFrame() {
1158 ObjectData
* ExecutionContext::getThis() {
1160 ActRec
* fp
= vmfp();
1161 if (fp
->skipFrame()) fp
= getPrevVMStateSkipFrame(fp
);
1162 if (fp
&& fp
->func()->cls() && fp
->hasThis()) {
1163 return fp
->getThis();
1168 Class
* ExecutionContext::getContextClass() {
1170 ActRec
* ar
= vmfp();
1171 assertx(ar
!= nullptr);
1172 if (ar
->skipFrame()) ar
= getPrevVMStateSkipFrame(ar
);
1173 return ar
? ar
->m_func
->cls() : nullptr;
1176 Class
* ExecutionContext::getParentContextClass() {
1177 if (Class
* ctx
= getContextClass()) {
1178 return ctx
->parent();
1183 const RepoOptions
& ExecutionContext::getRepoOptionsForCurrentFrame() const {
1186 if (auto const ar
= vmfp()) {
1187 auto const path
= ar
->func()->unit()->filepath();
1188 return RepoOptions::forFile(path
->data());
1190 return RepoOptions::defaults();
1193 void ExecutionContext::onLoadWithOptions(
1194 const char* f
, const RepoOptions
& opts
1196 if (!RuntimeOption::EvalFatalOnParserOptionMismatch
) return;
1197 if (!m_requestOptions
) {
1198 m_requestOptions
.emplace(opts
);
1201 if (m_requestOptions
!= opts
) {
1203 opts
.path().empty() ? "{default options}" : opts
.path().data();
1205 "Attempting to load file %s with incompatible parser settings from %s, "
1206 "this request is using parser settings from %s",
1207 f
, path
, m_requestOptions
->path().data()
1212 StringData
* ExecutionContext::getContainingFileName() {
1214 ActRec
* ar
= vmfp();
1215 if (ar
== nullptr) return staticEmptyString();
1216 if (ar
->skipFrame()) ar
= getPrevVMStateSkipFrame(ar
);
1217 if (ar
== nullptr) return staticEmptyString();
1218 Unit
* unit
= ar
->m_func
->unit();
1219 auto const path
= ar
->m_func
->originalFilename() ?
1220 ar
->m_func
->originalFilename() : unit
->filepath();
1221 return const_cast<StringData
*>(path
);
1224 int ExecutionContext::getLine() {
1226 ActRec
* ar
= vmfp();
1227 Unit
* unit
= ar
? ar
->m_func
->unit() : nullptr;
1228 Offset pc
= unit
? pcOff() : 0;
1229 if (ar
== nullptr) return -1;
1230 if (ar
->skipFrame()) ar
= getPrevVMStateSkipFrame(ar
, &pc
);
1231 if (ar
== nullptr || (unit
= ar
->m_func
->unit()) == nullptr) return -1;
1232 return unit
->getLineNumber(pc
);
1235 const StaticString
s___call("__call");
1236 const StaticString
s___callStatic("__callStatic");
1237 const StaticString
s_call_user_func("call_user_func");
1238 const StaticString
s_call_user_func_array("call_user_func_array");
1240 Array
ExecutionContext::getCallerInfo() {
1243 if (ar
->skipFrame()) {
1244 ar
= getPrevVMStateSkipFrame(ar
);
1245 if (!ar
) return empty_array();
1247 while (ar
->func()->name()->isame(s_call_user_func
.get())
1248 || ar
->func()->name()->isame(s_call_user_func_array
.get())) {
1249 ar
= getPrevVMState(ar
);
1250 if (ar
== nullptr) {
1251 return empty_darray();
1256 ar
= getPrevVMState(ar
, &pc
);
1257 while (ar
!= nullptr) {
1258 if (!ar
->func()->name()->isame(s_call_user_func
.get())
1259 && !ar
->func()->name()->isame(s_call_user_func_array
.get())) {
1260 auto const unit
= ar
->func()->unit();
1261 auto const path
= ar
->func()->originalFilename() ?
1262 ar
->func()->originalFilename() : unit
->filepath();
1264 if ((lineNumber
= unit
->getLineNumber(pc
)) != -1) {
1265 auto const cls
= ar
->func()->cls();
1266 if (cls
!= nullptr && !ar
->func()->isClosureBody()) {
1268 s_class
, const_cast<StringData
*>(cls
->name()),
1269 s_file
, const_cast<StringData
*>(path
),
1270 s_function
, const_cast<StringData
*>(ar
->func()->name()),
1275 s_file
, const_cast<StringData
*>(path
),
1276 s_function
, const_cast<StringData
*>(ar
->func()->name()),
1282 ar
= getPrevVMState(ar
, &pc
);
1284 return empty_darray();
1287 ActRec
* ExecutionContext::getFrameAtDepth(int frame
) {
1290 if (UNLIKELY(!fp
)) return nullptr;
1291 auto pc
= fp
->func()->unit()->offsetOf(vmpc());
1293 fp
= getPrevVMState(fp
, &pc
);
1294 if (UNLIKELY(!fp
)) return nullptr;
1295 if (UNLIKELY(fp
->skipFrame())) continue;
1298 while (fp
->skipFrame()) {
1299 fp
= getPrevVMState(fp
, &pc
);
1300 if (UNLIKELY(!fp
)) return nullptr;
1302 if (UNLIKELY(fp
->localsDecRefd())) return nullptr;
1303 auto const curOp
= fp
->func()->unit()->getOp(pc
);
1304 if (UNLIKELY(curOp
== Op::RetC
|| curOp
== Op::RetCSuspended
||
1305 curOp
== Op::RetM
|| curOp
== Op::CreateCont
||
1306 curOp
== Op::Await
)) {
1309 assertx(!fp
->magicDispatch());
1313 void ExecutionContext::setVar(StringData
* name
, tv_rval v
) {
1315 ActRec
*fp
= vmfp();
1317 if (fp
->skipFrame()) fp
= getPrevVMStateSkipFrame(fp
);
1318 if (fp
) fp
->getVarEnv()->set(name
, v
);
1321 Array
ExecutionContext::getLocalDefinedVariables(int frame
) {
1323 ActRec
*fp
= vmfp();
1324 for (; frame
> 0; --frame
) {
1326 fp
= getPrevVMState(fp
);
1328 return getDefinedVariables(fp
);
1331 bool ExecutionContext::setHeaderCallback(const Variant
& callback
) {
1332 if (cellAsVariant(g_context
->m_headerCallback
).toBoolean()) {
1333 // return false if a callback has already been set.
1336 cellAsVariant(g_context
->m_headerCallback
) = callback
;
1340 bool sideEffect(Op op
) {
1343 case Op::DefTypeAlias
:
1353 case Op::NullUninit
:
1357 case Op::RetCSuspended
:
1363 case Op::NewMixedArray
:
1364 case Op::NewLikeArrayL
:
1365 case Op::NewPackedArray
:
1366 case Op::NewStructArray
:
1367 case Op::NewStructDArray
:
1368 case Op::NewStructDict
:
1369 case Op::NewVecArray
:
1370 case Op::NewKeysetArray
:
1373 case Op::NewDictArray
:
1376 case Op::AssertRATL
:
1377 case Op::AssertRATStk
:
1385 * RetC has no side-effect only if when it is the last statement,
1386 * and it precedent op is Int 1, like return 0 in c/c++
1388 bool checkForRet(Op op
, bool isLast
, PC lastOp
) {
1390 if (op
== Op::RetC
) {
1391 return !isLast
|| decode_op(lastOp
) != Op::Int
||
1392 decode_raw
<int64_t>(lastOp
) != 1;
1398 * PopC has no side-effect only if it precedes by a DefCns ops, e.g.
1401 bool checkPopc(Op op
, PC lastOp
) {
1402 if (op
== Op::PopC
) {
1403 return peek_op(lastOp
) != Op::DefCns
;
1408 void pseudomainHelper(const Unit
* unit
, bool callByHPHPInvoke
) {
1409 auto pseudomain
= unit
->getMain(nullptr);
1410 auto e
= pseudomain
->getEntry();
1411 bool isLast
= false;
1413 while (e
< unit
->entry() + pseudomain
->past()) {
1414 if (e
+ instrLen(e
) >= unit
->entry() + pseudomain
->past()) {
1417 if (checkPopc(peek_op(e
), lastOp
) ||
1418 sideEffect(peek_op(e
)) ||
1419 checkForRet(peek_op(e
), isLast
, lastOp
)) {
1420 if (callByHPHPInvoke
) {
1421 if (RuntimeOption::EvalWarnOnRealPseudomain
) {
1422 raise_warning("The top-level code has side effects in %s"
1423 " which is called by top level code",
1424 unit
->filepath()->data());
1428 if (RuntimeOption::EvalWarnOnUncalledPseudomain
== 1) {
1429 raise_warning("The top-level code has side effect in %s "
1430 "by top level code that isn't invoked by pseudomain",
1431 unit
->filepath()->data());
1433 } else if (RuntimeOption::EvalWarnOnUncalledPseudomain
== 2) {
1435 folly::sformat("The top-level code has side effect in %s"
1436 "by top level code that isn't invoked by pseudomain,"
1438 unit
->filepath()->data()).c_str());
1442 if (peek_op(e
) != Op::AssertRATStk
&& peek_op(e
) != Op::AssertRATL
) {
1449 const static StaticString
s_entry_point("__SystemLib\\enter_async_entry_point");
1451 TypedValue
ExecutionContext::invokeUnit(const Unit
* unit
,
1452 bool callByHPHPInvoke
) {
1453 checkHHConfig(unit
);
1455 if (UNLIKELY(!RuntimeOption::EnablePHP
) && !unit
->isHHFile()) {
1456 throw PhpNotSupportedException(unit
->filepath()->data());
1459 auto const func
= unit
->getMain(nullptr);
1460 auto ret
= invokeFunc(func
, init_null_variant
, nullptr, nullptr,
1461 m_globalVarEnv
, nullptr, InvokePseudoMain
);
1463 pseudomainHelper(unit
, callByHPHPInvoke
);
1465 auto it
= unit
->getCachedEntryPoint();
1466 if (callByHPHPInvoke
&& it
!= nullptr) {
1467 if (it
->isAsync()) {
1469 Unit::lookupFunc(s_entry_point
.get()),
1470 make_vec_array(VarNR
{it
->fullDisplayName()}),
1471 nullptr, nullptr, nullptr, nullptr, InvokeNormal
1474 invokeFunc(it
, init_null_variant
, nullptr, nullptr,
1475 nullptr, nullptr, InvokeNormal
);
1481 void ExecutionContext::syncGdbState() {
1482 if (RuntimeOption::EvalJit
&& !RuntimeOption::EvalJitNoGdb
) {
1483 Debug::DebugInfo::Get()->debugSync();
1487 void ExecutionContext::pushVMState(Cell
* savedSP
) {
1488 if (UNLIKELY(!vmfp())) {
1490 assertx(m_nestedVMs
.size() == 0);
1494 TRACE(3, "savedVM: %p %p %p %p\n", vmpc(), vmfp(), vmFirstAR(), savedSP
);
1495 auto& savedVM
= m_nestedVMs
.alloc_back();
1496 savedVM
.pc
= vmpc();
1497 savedVM
.fp
= vmfp();
1498 savedVM
.firstAR
= vmFirstAR();
1499 savedVM
.sp
= savedSP
;
1500 savedVM
.mInstrState
= vmMInstrState();
1501 savedVM
.jitCalledFrame
= vmJitCalledFrame();
1502 savedVM
.jitReturnAddr
= vmJitReturnAddr();
1505 if (debug
&& savedVM
.fp
&&
1506 savedVM
.fp
->m_func
&&
1507 savedVM
.fp
->m_func
->unit()) {
1508 // Some asserts and tracing.
1509 const Func
* func
= savedVM
.fp
->m_func
;
1510 /* bound-check asserts in offsetOf */
1511 func
->unit()->offsetOf(savedVM
.pc
);
1512 TRACE(3, "pushVMState: saving frame %s pc %p off %d fp %p\n",
1513 func
->name()->data(),
1515 func
->unit()->offsetOf(savedVM
.pc
),
1520 void ExecutionContext::popVMState() {
1521 if (UNLIKELY(m_nestedVMs
.empty())) {
1525 vmFirstAR() = nullptr;
1529 assertx(m_nestedVMs
.size() >= 1);
1531 VMState
&savedVM
= m_nestedVMs
.back();
1532 vmpc() = savedVM
.pc
;
1533 vmfp() = savedVM
.fp
;
1534 vmFirstAR() = savedVM
.firstAR
;
1535 vmStack().top() = savedVM
.sp
;
1536 vmMInstrState() = savedVM
.mInstrState
;
1537 vmJitCalledFrame() = savedVM
.jitCalledFrame
;
1538 vmJitReturnAddr() = savedVM
.jitReturnAddr
;
1542 savedVM
.fp
->m_func
&&
1543 savedVM
.fp
->m_func
->unit()) {
1544 const Func
* func
= savedVM
.fp
->m_func
;
1545 (void) /* bound-check asserts in offsetOf */
1546 func
->unit()->offsetOf(savedVM
.pc
);
1547 TRACE(3, "popVMState: restoring frame %s pc %p off %d fp %p\n",
1548 func
->name()->data(),
1550 func
->unit()->offsetOf(savedVM
.pc
),
1555 m_nestedVMs
.pop_back();
1558 TRACE(1, "Reentry: exit fp %p pc %p\n", vmfp(), vmpc());
1561 void ExecutionContext::ExcLoggerHook::operator()(
1562 const char* header
, const char* msg
, const char* ending
1571 s_php_namespace("<?php namespace "),
1572 s_hh_namespace("<?hh namespace "),
1573 s_curly_return(" { return "),
1574 s_semicolon_curly("; }"),
1575 s_php_return("<?php return "),
1576 s_hh_return("<?hh return "),
1578 s_stdclass("stdclass");
1580 void ExecutionContext::requestInit() {
1581 assertx(SystemLib::s_unit
);
1584 VarEnv::createGlobal();
1585 vmStack().requestInit();
1586 ResourceHdr::resetMaxId();
1587 jit::tc::requestInit();
1589 if (RuntimeOption::EvalJitEnableRenameFunction
) {
1590 assertx(SystemLib::s_anyNonPersistentBuiltins
);
1594 * The normal case for production mode is that all builtins are
1595 * persistent, and every systemlib unit is accordingly going to be
1598 * However, if we have rename_function generally enabled, or if any
1599 * builtin functions were specified as interceptable at
1600 * repo-generation time, we'll actually need to merge systemlib on
1601 * every request because some of the builtins will not be marked
1604 if (UNLIKELY(SystemLib::s_anyNonPersistentBuiltins
)) {
1605 SystemLib::s_unit
->merge();
1606 SystemLib::mergePersistentUnits();
1607 if (SystemLib::s_hhas_unit
) SystemLib::s_hhas_unit
->merge();
1609 // System units are merge only, and everything is persistent.
1610 assertx(SystemLib::s_unit
->isEmpty());
1611 assertx(!SystemLib::s_hhas_unit
|| SystemLib::s_hhas_unit
->isEmpty());
1614 profileRequestStart();
1616 HHProf::Request::StartProfiling();
1619 Class
* cls
= NamedEntity::get(s_stdclass
.get())->clsList();
1621 assertx(cls
== SystemLib::s_stdclassClass
);
1624 if (Logger::UseRequestLog
) Logger::SetThreadHook(&m_logger_hook
);
1626 // Needs to be last (or nearly last): might cause unit merging to call an
1627 // extension function in the VM; this is bad if systemlib itself hasn't been
1629 autoTypecheckRequestInit();
1632 void ExecutionContext::requestExit() {
1633 autoTypecheckRequestExit();
1634 HHProf::Request::FinishProfiling();
1638 vmStack().requestExit();
1639 profileRequestEnd();
1640 EventHook::Disable();
1644 if (m_globalVarEnv
) {
1645 req::destroy_raw(m_globalVarEnv
);
1646 m_globalVarEnv
= nullptr;
1649 if (!m_lastError
.isNull()) {
1653 m_deferredErrors
= Array::CreateVec();
1655 if (Logger::UseRequestLog
) Logger::SetThreadHook(nullptr);
1656 if (m_requestTrace
) record_trace(std::move(*m_requestTrace
));
1660 * Shared implementation for invokeFunc{,Few}().
1662 * The `doCheckStack' and `doInitArgs' callbacks should return truthy in order
1663 * to short-circuit the rest of invokeFuncImpl() and return early, else they
1664 * should return falsey.
1666 * The `doInitArgs' and `doEnterVM' callbacks take an ActRec* argument
1667 * corresponding to the reentry frame.
1669 template<class FStackCheck
, class FInitArgs
, class FEnterVM
>
1671 TypedValue
ExecutionContext::invokeFuncImpl(const Func
* f
,
1672 ObjectData
* thiz
, Class
* cls
,
1673 uint32_t argc
, StringData
* invName
,
1675 FStackCheck doStackCheck
,
1676 FInitArgs doInitArgs
,
1677 FEnterVM doEnterVM
) {
1679 // If `f' is a regular function, `thiz' and `cls' must be null.
1680 assertx(IMPLIES(!f
->implCls(), (!thiz
&& !cls
)));
1681 // If `f' is a method, either `thiz' or `cls' must be non-null.
1682 assertx(IMPLIES(f
->preClass(), thiz
|| cls
));
1683 // If `f' is a static method, thiz must be null.
1684 assertx(IMPLIES(f
->isStaticInPrologue(), !thiz
));
1685 // invName should only be non-null if we are calling __call or __callStatic.
1686 assertx(IMPLIES(invName
, f
->name()->isame(s___call
.get()) ||
1687 f
->name()->isame(s___callStatic
.get())));
1690 auto const reentrySP
= vmStack().top();
1692 if (dynamic
) callerDynamicCallChecks(f
);
1694 if (thiz
!= nullptr) thiz
->incRefCount();
1697 if (doStackCheck(retval
)) return retval
;
1699 if (UNLIKELY(f
->takesInOutParams())) {
1700 for (auto i
= f
->numInOutParams(); i
> 0; --i
) vmStack().pushNull();
1703 ActRec
* ar
= vmStack().allocA();
1704 ar
->setReturnVMExit();
1713 ar
->initNumArgs(argc
);
1714 if (dynamic
) ar
->setDynamicCall();
1716 if (UNLIKELY(invName
!= nullptr)) {
1717 ar
->setMagicDispatch(invName
);
1722 if (UNLIKELY(f
->takesInOutParams())) {
1727 if (vmfp() == nullptr) {
1728 TRACE(1, "Reentry: enter %s(%p) from top-level\n",
1729 f
->name()->data(), ar
);
1731 TRACE(1, "Reentry: enter %s(pc %p ar %p) from %s(%p)\n",
1732 f
->name()->data(), vmpc(), ar
,
1733 vmfp()->m_func
? vmfp()->m_func
->name()->data()
1740 if (doInitArgs(ar
, retval
)) return retval
;
1742 while (vmStack().top() != (void*)ar
) {
1750 pushVMState(reentrySP
);
1753 vmStack().top() == reentrySP
,
1754 "vmsp() mismatch around reentry: before @ {}, after @ {}",
1755 reentrySP
, vmStack().top()
1762 // Pop off any placeholder in-out stack slots if we throw from doEnterVM
1763 // to ensure the stack pointer matches.
1764 if (UNLIKELY(f
->takesInOutParams())) {
1765 for (auto i
= f
->numInOutParams(); i
> 0; --i
) vmStack().popTV();
1771 // `retptr' might point somewhere that is affected by {push,pop}VMState(),
1772 // so don't write to it until after we pop the nested VM state.
1773 if (UNLIKELY(f
->takesInOutParams())) {
1774 VArrayInit
varr(f
->numInOutParams() + 1);
1775 for (uint32_t i
= 0; i
< f
->numInOutParams() + 1; ++i
) {
1776 varr
.append(*vmStack().topTV());
1779 auto arr
= varr
.toArray();
1780 retval
= make_array_like_tv(arr
.detach());
1782 tvCopy(*vmStack().topTV(), retval
);
1783 vmStack().discard();
1790 * Enter VM by calling action(), which invokes a function or resumes
1791 * an async function. The 'ar' argument points to an ActRec of the
1792 * invoked/resumed function.
1794 template<class Action
>
1795 static inline void enterVMCustomHandler(ActRec
* ar
, Action action
) {
1797 assertx(!ar
->sfp());
1798 assertx(isReturnHelper(reinterpret_cast<void*>(ar
->m_savedRip
)));
1799 assertx(ar
->m_callOff
== 0);
1801 auto ec
= &*g_context
;
1802 DEBUG_ONLY
int faultDepth
= ec
->m_faults
.size();
1803 SCOPE_EXIT
{ assertx(ec
->m_faults
.size() == faultDepth
); };
1806 vmJitCalledFrame() = nullptr;
1807 vmJitReturnAddr() = 0;
1812 exception_handler(enterVMAtCurPC
);
1816 template<class Action
>
1817 static inline void enterVM(ActRec
* ar
, Action action
) {
1818 enterVMCustomHandler(ar
, [&] { exception_handler(action
); });
1821 TypedValue
ExecutionContext::invokeFunc(const Func
* f
,
1822 const Variant
& args_
,
1823 ObjectData
* thiz
/* = NULL */,
1824 Class
* cls
/* = NULL */,
1825 VarEnv
* varEnv
/* = NULL */,
1826 StringData
* invName
/* = NULL */,
1827 InvokeFlags flags
/* = InvokeNormal */,
1828 bool dynamic
/* = true */,
1829 bool checkRefAnnot
/* = false */) {
1830 const auto& args
= *args_
.toCell();
1831 assertx(isContainerOrNull(args
));
1833 auto const argc
= cellIsNull(&args
) ? 0 : getContainerSize(args
);
1834 // If we are inheriting a variable environment, then `args' must be empty.
1835 assertx(IMPLIES(varEnv
, argc
== 0));
1837 auto const doCheckStack
= [&](TypedValue
& retval
) {
1838 // We must do a stack overflow check for leaf functions on re-entry,
1839 // because we won't have checked that the stack is deep enough for a
1840 // leaf function /after/ re-entry, and the prologue for the leaf
1841 // function will not make a check.
1842 if (f
->isPhpLeafFn() ||
1843 !(f
->numParams() <= kStackCheckReenterPadding
- kNumActRecCells
)) {
1844 // Check both the native stack and VM stack for overflow.
1845 checkStack(vmStack(), f
,
1846 kNumActRecCells
/* numParams is included in f->maxStackCells */);
1848 // invokeFunc() must always check the native stack for overflow no
1853 // Handle includes of pseudomains.
1854 if (flags
& InvokePseudoMain
) {
1855 assertx(f
->isPseudoMain());
1856 assertx(cellIsNull(&args
) || !getContainerSize(args
));
1858 auto toMerge
= f
->unit();
1860 if (toMerge
->isMergeOnly()) {
1861 Stats::inc(Stats::PseudoMain_Skipped
);
1862 retval
= *toMerge
->getMainReturn();
1865 Stats::inc(Stats::PseudoMain_Executed
);
1870 auto const doInitArgs
= [&] (ActRec
* ar
, TypedValue
& retval
) {
1872 auto const& prepArgs
= cellIsNull(&args
)
1873 ? make_array_like_tv(staticEmptyVArray())
1875 auto prepResult
= prepareArrayArgs(ar
, prepArgs
, vmStack(), 0,
1876 &retval
, checkRefAnnot
);
1877 if (UNLIKELY(!prepResult
)) {
1878 assertx(KindOfNull
== retval
.m_type
);
1885 auto const doEnterVM
= [&] (ActRec
* ar
) {
1889 varEnv
? StackArgsState::Untrimmed
: StackArgsState::Trimmed
,
1895 return invokeFuncImpl(f
, thiz
, cls
, argc
, invName
,
1896 dynamic
&& !(flags
& InvokePseudoMain
),
1897 doCheckStack
, doInitArgs
, doEnterVM
);
1900 TypedValue
ExecutionContext::invokeFuncFew(const Func
* f
,
1902 StringData
* invName
,
1904 const TypedValue
* argv
,
1905 bool dynamic
/* = true */) {
1906 auto const doCheckStack
= [&](TypedValue
&) {
1907 // See comments in invokeFunc().
1908 if (f
->isPhpLeafFn() ||
1909 !(argc
<= kStackCheckReenterPadding
- kNumActRecCells
)) {
1910 checkStack(vmStack(), f
, argc
+ kNumActRecCells
);
1917 auto const doInitArgs
= [&](ActRec
* /*ar*/, TypedValue
&) {
1918 for (ssize_t i
= 0; i
< argc
; ++i
) {
1919 const TypedValue
*from
= &argv
[i
];
1920 TypedValue
*to
= vmStack().allocTV();
1921 if (LIKELY(!isRefType(from
->m_type
) || !f
->byRef(i
))) {
1922 cellDup(*tvToCell(from
), *to
);
1930 auto const doEnterVM
= [&] (ActRec
* ar
) {
1931 enterVM(ar
, [&] { enterVMAtFunc(ar
, StackArgsState::Untrimmed
, nullptr); });
1934 return invokeFuncImpl(f
,
1935 ActRec::decodeThis(thisOrCls
),
1936 ActRec::decodeClass(thisOrCls
),
1937 argc
, invName
, dynamic
,
1938 doCheckStack
, doInitArgs
, doEnterVM
);
1941 static void prepareAsyncFuncEntry(ActRec
* enterFnAr
, Resumable
* resumable
) {
1943 assertx(enterFnAr
->func()->isAsync());
1944 assertx(enterFnAr
->resumed());
1948 vmpc() = vmfp()->func()->unit()->at(resumable
->resumeOffset());
1949 assertx(vmfp()->func()->contains(vmpc()));
1950 EventHook::FunctionResumeAwait(enterFnAr
);
1953 void ExecutionContext::resumeAsyncFunc(Resumable
* resumable
,
1954 ObjectData
* freeObj
,
1955 const Cell awaitResult
) {
1956 assertx(tl_regState
== VMRegState::CLEAN
);
1957 SCOPE_EXIT
{ assertx(tl_regState
== VMRegState::CLEAN
); };
1959 auto fp
= resumable
->actRec();
1960 // We don't need to check for space for the ActRec (unlike generally
1961 // in normal re-entry), because the ActRec isn't on the stack.
1962 checkStack(vmStack(), fp
->func(), 0);
1964 Cell
* savedSP
= vmStack().top();
1965 cellDup(awaitResult
, *vmStack().allocC());
1967 // decref after awaitResult is on the stack
1970 pushVMState(savedSP
);
1971 SCOPE_EXIT
{ popVMState(); };
1974 prepareAsyncFuncEntry(fp
, resumable
);
1976 const bool useJit
= RID().getJit();
1977 if (LIKELY(useJit
&& resumable
->resumeAddr())) {
1978 Stats::inc(Stats::VMEnter
);
1979 jit::enterTCAfterPrologue(resumable
->resumeAddr());
1986 void ExecutionContext::resumeAsyncFuncThrow(Resumable
* resumable
,
1987 ObjectData
* freeObj
,
1988 ObjectData
* exception
) {
1990 assertx(exception
->instanceof(SystemLib::s_ThrowableClass
));
1991 assertx(tl_regState
== VMRegState::CLEAN
);
1992 SCOPE_EXIT
{ assertx(tl_regState
== VMRegState::CLEAN
); };
1994 auto fp
= resumable
->actRec();
1995 checkStack(vmStack(), fp
->func(), 0);
1997 // decref after we hold reference to the exception
1998 Object
e(exception
);
2001 pushVMState(vmStack().top());
2002 SCOPE_EXIT
{ popVMState(); };
2004 enterVMCustomHandler(fp
, [&] {
2005 prepareAsyncFuncEntry(fp
, resumable
);
2007 unwindPhp(exception
);
2011 ActRec
* ExecutionContext::getPrevVMState(const ActRec
* fp
,
2012 Offset
* prevPc
/* = NULL */,
2013 TypedValue
** prevSp
/* = NULL */,
2014 bool* fromVMEntry
/* = NULL */,
2015 uint64_t* jitReturnAddr
/* = NULL */) {
2016 if (fp
== nullptr) {
2019 ActRec
* prevFp
= fp
->sfp();
2020 if (LIKELY(prevFp
!= nullptr)) {
2022 if (UNLIKELY(fp
->resumed())) {
2023 assertx(fp
->func()->isGenerator());
2024 *prevSp
= (TypedValue
*)prevFp
- prevFp
->func()->numSlotsInFrame();
2026 *prevSp
= (TypedValue
*)(fp
+ 1);
2029 if (prevPc
) *prevPc
= prevFp
->func()->base() + fp
->m_callOff
;
2030 if (fromVMEntry
) *fromVMEntry
= false;
2033 // Linear search from end of m_nestedVMs. In practice, we're probably
2034 // looking for something recently pushed.
2035 int i
= m_nestedVMs
.size() - 1;
2036 ActRec
* firstAR
= vmFirstAR();
2037 while (i
>= 0 && firstAR
!= fp
) {
2038 firstAR
= m_nestedVMs
[i
--].firstAR
;
2040 if (i
== -1) return nullptr;
2041 const VMState
& vmstate
= m_nestedVMs
[i
];
2042 prevFp
= vmstate
.fp
;
2044 assertx(prevFp
->func()->unit());
2045 if (prevSp
) *prevSp
= vmstate
.sp
;
2047 *prevPc
= prevFp
->func()->unit()->offsetOf(vmstate
.pc
);
2049 if (fromVMEntry
) *fromVMEntry
= true;
2050 if (jitReturnAddr
) *jitReturnAddr
= (uint64_t)vmstate
.jitReturnAddr
;
2055 Instantiate hoistable classes and functions.
2056 If there is any more work left to do, setup a
2057 new frame ready to execute the pseudomain.
2059 return true iff the pseudomain needs to be executed.
2061 bool ExecutionContext::evalUnit(Unit
* unit
, PC callPC
, PC
& pc
, int funcType
) {
2064 if (unit
->isMergeOnly()) {
2065 Stats::inc(Stats::PseudoMain_Skipped
);
2066 *vmStack().allocTV() = *unit
->getMainReturn();
2069 Stats::inc(Stats::PseudoMain_Executed
);
2071 ActRec
* ar
= vmStack().allocA();
2072 auto const cls
= vmfp()->func()->cls();
2073 auto const func
= unit
->getMain(cls
);
2074 assertx(!func
->isCPPBuiltin());
2077 ar
->setThisOrClass(vmfp()->getThisOrClass());
2078 if (ar
->hasThis()) ar
->getThis()->incRefCount();
2084 ar
->setReturn(vmfp(), callPC
, jit::tc::ustubs().retHelper
);
2085 pushFrameSlots(func
);
2087 auto prevFp
= vmfp();
2088 if (UNLIKELY(prevFp
->skipFrame())) {
2089 prevFp
= g_context
->getPrevVMStateSkipFrame(prevFp
);
2092 assertx(prevFp
->func()->attrs() & AttrMayUseVV
);
2093 if (!prevFp
->hasVarEnv()) {
2094 prevFp
->setVarEnv(VarEnv::createLocal(prevFp
));
2096 ar
->m_varEnv
= prevFp
->m_varEnv
;
2097 ar
->m_varEnv
->enterFP(prevFp
, ar
);
2100 pc
= func
->getEntry();
2102 bool ret
= EventHook::FunctionCall(vmfp(), funcType
);
2104 checkStack(vmStack(), func
, 0);
2108 Variant
ExecutionContext::getEvaledArg(const StringData
* val
,
2109 const String
& namespacedName
,
2110 const Unit
* funcUnit
) {
2111 auto key
= StrNR(val
);
2113 if (m_evaledArgs
.get()) {
2114 auto const arg
= m_evaledArgs
.get()->get(key
);
2115 if (!arg
.is_dummy()) return Variant::wrap(arg
.tv());
2119 int pos
= namespacedName
.rfind('\\');
2121 auto ns
= namespacedName
.substr(0, pos
);
2122 code
= (funcUnit
->isHHFile() ? s_hh_namespace
: s_php_namespace
) +
2123 ns
+ s_curly_return
+ key
+ s_semicolon_curly
;
2125 code
= (funcUnit
->isHHFile() ? s_hh_return
: s_php_return
) +
2128 Unit
* unit
= compileEvalString(code
.get());
2129 assertx(unit
!= nullptr);
2130 // Default arg values are not currently allowed to depend on class context.
2131 auto v
= Variant::attach(
2132 g_context
->invokeFunc(unit
->getMain(nullptr),
2133 init_null_variant
, nullptr, nullptr, nullptr, nullptr,
2136 SuppressHACFalseyPromoteNotices shacn
;
2137 auto const lv
= m_evaledArgs
.lvalAt(key
, AccessFlags::Key
);
2138 tvSet(*v
.asTypedValue(), lv
);
2139 return Variant::wrap(lv
.tv());
2142 void ExecutionContext::recordLastError(const Exception
& e
, int errnum
) {
2143 m_lastError
= String(e
.getMessage());
2144 m_lastErrorNum
= errnum
;
2145 m_lastErrorPath
= String::attach(getContainingFileName());
2146 m_lastErrorLine
= getLine();
2147 if (auto const ee
= dynamic_cast<const ExtendedException
*>(&e
)) {
2148 m_lastErrorPath
= ee
->getFileAndLine().first
;
2149 m_lastErrorLine
= ee
->getFileAndLine().second
;
2153 void ExecutionContext::clearLastError() {
2154 m_lastError
= String();
2156 m_lastErrorPath
= staticEmptyString();
2157 m_lastErrorLine
= 0;
2160 void ExecutionContext::enqueueAPCHandle(APCHandle
* handle
, size_t size
) {
2161 assertx(handle
->isUncounted());
2162 if (RuntimeOption::EvalGCForAPC
) {
2163 // Register handle with APCGCManager
2164 // And resursively find all allocations belong to handle, register them too
2165 APCGCManager::getInstance().registerPendingDeletion(handle
, size
);
2167 m_apcHandles
.push_back(handle
);
2168 m_apcMemSize
+= size
;
2171 // Treadmill solution for the SharedVariant memory management
2173 struct FreedAPCHandle
{
2174 explicit FreedAPCHandle(std::vector
<APCHandle
*>&& shandles
, size_t size
)
2175 : m_memSize(size
), m_apcHandles(std::move(shandles
))
2178 if (RuntimeOption::EvalGCForAPC
) {
2179 // Treadmill ask APCGCManager to free the handles
2180 APCGCManager::getInstance().freeAPCHandles(m_apcHandles
);
2182 for (auto handle
: m_apcHandles
) {
2183 APCTypedValue::fromHandle(handle
)->deleteUncounted();
2186 APCStats::getAPCStats().removePendingDelete(m_memSize
);
2190 std::vector
<APCHandle
*> m_apcHandles
;
2194 void ExecutionContext::manageAPCHandle() {
2195 assertx(apcExtension::UseUncounted
|| m_apcHandles
.size() == 0);
2196 if (m_apcHandles
.size() > 0) {
2197 std::vector
<APCHandle
*> handles
;
2198 handles
.swap(m_apcHandles
);
2200 FreedAPCHandle(std::move(handles
), m_apcMemSize
)
2202 APCStats::getAPCStats().addPendingDelete(m_apcMemSize
);
2206 // Evaled units have a footprint in the TC and translation metadata. The
2207 // applications we care about tend to have few, short, stereotyped evals,
2208 // where the same code keeps getting eval'ed over and over again; so we
2209 // keep around units for each eval'ed string, so that the TC space isn't
2210 // wasted on each eval.
2211 typedef RankedCHM
<StringData
*, HPHP::Unit
*,
2212 StringDataHashCompare
,
2213 RankEvaledUnits
> EvaledUnitsMap
;
2214 static EvaledUnitsMap s_evaledUnits
;
2215 Unit
* ExecutionContext::compileEvalString(
2217 const char* evalFilename
/* = nullptr */) {
2218 EvaledUnitsMap::accessor acc
;
2219 // Promote this to a static string; otherwise it may get swept
2221 code
= makeStaticString(code
);
2222 if (s_evaledUnits
.insert(acc
, code
)) {
2223 acc
->second
= compile_string(
2227 Native::s_noNativeFuncs
,
2228 getRepoOptionsForCurrentFrame()
2234 ExecutionContext::EvaluationResult
2235 ExecutionContext::evalPHPDebugger(StringData
* code
, int frame
) {
2236 // The code has "<?php" prepended already
2237 auto unit
= compile_debugger_string(code
->data(), code
->size(),
2238 getRepoOptionsForCurrentFrame());
2239 if (unit
== nullptr) {
2240 raise_error("Syntax error");
2241 return {true, init_null_variant
, "Syntax error"};
2244 return evalPHPDebugger(unit
, frame
);
2247 ExecutionContext::EvaluationResult
2248 ExecutionContext::evalPHPDebugger(Unit
* unit
, int frame
) {
2249 always_assert(!RuntimeOption::RepoAuthoritative
);
2251 // Do not JIT this unit, we are using it exactly once.
2252 unit
->setInterpretOnly();
2258 if (fp
->skipFrame()) fp
= getPrevVMStateSkipFrame(fp
);
2259 for (; frame
> 0; --frame
) {
2260 auto prevFp
= getPrevVMStateSkipFrame(fp
);
2262 // To be safe in case we failed to get prevFp. This would mean we've
2263 // been asked to eval in a frame which is beyond the top of the stack.
2264 // This suggests the debugger client has made an error.
2269 if (!fp
->hasVarEnv()) {
2270 fp
->setVarEnv(VarEnv::createLocal(fp
));
2273 ObjectData
*this_
= nullptr;
2274 // NB: the ActRec and function within the AR may have different classes. The
2275 // class in the ActRec is the type used when invoking the function (i.e.,
2276 // Derived in Derived::Foo()) while the class obtained from the function is
2277 // the type that declared the function Foo, which may be Base. We need both
2278 // the class to match any object that this function may have been invoked on,
2279 // and we need the class from the function execution is stopped in.
2280 Class
*frameClass
= nullptr;
2281 Class
*functionClass
= nullptr;
2283 functionClass
= fp
->m_func
->cls();
2284 if (functionClass
) {
2285 if (fp
->hasThis()) {
2286 this_
= fp
->getThis();
2287 } else if (fp
->hasClass()) {
2288 frameClass
= fp
->getClass();
2291 phpDebuggerEvalHook(fp
->m_func
);
2294 const static StaticString
s_cppException("Hit an exception");
2295 const static StaticString
s_phpException("Hit a php exception");
2296 const static StaticString
s_exit("Hit exit");
2297 const static StaticString
s_fatal("Hit fatal");
2298 std::ostringstream errorString
;
2301 // Find a suitable PC to use when switching to the target frame. If the target
2302 // is the current frame, this is just vmpc(). For other cases, this will
2303 // generally be the address of a call from that frame's function. If we can't
2304 // find the target frame (because it lies deeper in the stack), then just use
2305 // the target frame's func's entry point.
2306 auto const findSuitablePC
= [this](const ActRec
* target
){
2307 if (auto fp
= vmfp()) {
2308 if (fp
== target
) return vmpc();
2310 auto prevFp
= getPrevVMState(fp
);
2312 if (prevFp
== target
) return prevFp
->func()->getEntry() + fp
->m_callOff
;
2316 return target
->func()->getEntry();
2320 // Start with the correct parent FP so that VarEnv can properly exitFP().
2321 // Note that if the same VarEnv is used across multiple frames, the most
2322 // recent FP must be used. This can happen if we are trying to debug
2323 // an eval() call or a call issued by debugger itself.
2325 // We also need to change vmpc() to match, since we assert in a few places
2326 // that the vmpc() lies within vmfp()'s code.
2327 auto savedFP
= vmfp();
2328 auto savedPC
= vmpc();
2330 auto newFp
= fp
->m_varEnv
->getFP();
2331 assertx(!newFp
->skipFrame());
2332 vmpc() = findSuitablePC(newFp
);
2335 SCOPE_EXIT
{ vmpc() = savedPC
; vmfp() = savedFP
; };
2337 // Invoke the given PHP, possibly specialized to match the type of the
2338 // current function on the stack, optionally passing a this pointer or
2339 // class used to execute the current function.
2340 return {false, Variant::attach(
2341 invokeFunc(unit
->getMain(functionClass
), init_null_variant
,
2342 this_
, frameClass
, fp
? fp
->m_varEnv
: nullptr, nullptr,
2345 } catch (FatalErrorException
& e
) {
2346 errorString
<< s_fatal
.data();
2347 errorString
<< " : ";
2348 errorString
<< e
.getMessage().c_str();
2349 errorString
<< "\n";
2350 stack
= ExtendedLogger::StringOfStackTrace(e
.getBacktrace());
2351 } catch (ExitException
& e
) {
2352 errorString
<< s_exit
.data();
2353 errorString
<< " : ";
2354 errorString
<< *rl_exit_code
;
2355 } catch (Eval::DebuggerException
& e
) {
2356 } catch (Exception
& e
) {
2357 errorString
<< s_cppException
.data();
2358 errorString
<< " : ";
2359 errorString
<< e
.getMessage().c_str();
2360 ExtendedException
* ee
= dynamic_cast<ExtendedException
*>(&e
);
2362 errorString
<< "\n";
2363 stack
= ExtendedLogger::StringOfStackTrace(ee
->getBacktrace());
2365 } catch (Object
&e
) {
2366 errorString
<< s_phpException
.data();
2367 errorString
<< " : ";
2369 errorString
<< e
->invokeToString().data();
2371 errorString
<< e
->getVMClass()->name()->data();
2374 errorString
<< s_cppException
.data();
2377 auto errorStr
= errorString
.str();
2378 g_context
->write(errorStr
);
2379 if (!stack
.empty()) {
2380 g_context
->write(stack
.c_str());
2383 return {true, init_null_variant
, errorStr
};
2386 void ExecutionContext::enterDebuggerDummyEnv() {
2387 static Unit
* s_debuggerDummy
= compile_debugger_string(
2388 "<?php?>", 7, RepoOptions::defaults()
2390 // Ensure that the VM stack is completely empty (vmfp() should be null)
2391 // and that we're not in a nested VM (reentrancy)
2392 assertx(vmfp() == nullptr);
2393 assertx(m_nestedVMs
.size() == 0);
2394 assertx(m_nesting
== 0);
2395 assertx(vmStack().count() == 0);
2396 ActRec
* ar
= vmStack().allocA();
2397 ar
->m_func
= s_debuggerDummy
->getMain(nullptr);
2400 ar
->setReturnVMExit();
2402 vmpc() = s_debuggerDummy
->entry();
2404 vmfp()->setVarEnv(m_globalVarEnv
);
2405 m_globalVarEnv
->enterFP(nullptr, vmfp());
2408 void ExecutionContext::exitDebuggerDummyEnv() {
2409 assertx(m_globalVarEnv
);
2410 // Ensure that vmfp() is valid
2411 assertx(vmfp() != nullptr);
2412 // Ensure that vmfp() points to the only frame on the call stack.
2413 // In other words, make sure there are no VM frames directly below
2414 // this one and that we are not in a nested VM (reentrancy)
2415 assertx(!vmfp()->sfp());
2416 assertx(m_nestedVMs
.size() == 0);
2417 assertx(m_nesting
== 0);
2418 // Teardown the frame we erected by enterDebuggerDummyEnv()
2419 const Func
* func
= vmfp()->m_func
;
2421 vmfp()->setLocalsDecRefd();
2422 frame_free_locals_no_hook(vmfp());
2424 vmStack().ndiscard(func
->numSlotsInFrame());
2425 vmStack().discardAR();
2426 // After tearing down this frame, the VM stack should be completely empty
2427 assertx(vmStack().count() == 0);
2432 ThrowAllErrorsSetter::ThrowAllErrorsSetter() {
2433 m_throwAllErrors
= g_context
->getThrowAllErrors();
2434 g_context
->setThrowAllErrors(true);
2437 ThrowAllErrorsSetter::~ThrowAllErrorsSetter() {
2438 g_context
->setThrowAllErrors(m_throwAllErrors
);