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/array-iterator-defs.h"
38 #include "hphp/runtime/base/apc-gc-manager.h"
39 #include "hphp/runtime/base/memory-manager.h"
40 #include "hphp/runtime/base/sweepable.h"
41 #include "hphp/runtime/base/builtin-functions.h"
42 #include "hphp/runtime/base/comparisons.h"
43 #include "hphp/runtime/base/externals.h"
44 #include "hphp/runtime/base/program-functions.h"
45 #include "hphp/runtime/base/runtime-option.h"
46 #include "hphp/runtime/base/tv-refcount.h"
47 #include "hphp/runtime/base/tv-type.h"
48 #include "hphp/runtime/base/unit-cache.h"
49 #include "hphp/runtime/base/system-profiler.h"
50 #include "hphp/runtime/base/container-functions.h"
51 #include "hphp/runtime/base/hhprof.h"
52 #include "hphp/runtime/base/apc-stats.h"
53 #include "hphp/runtime/base/apc-typed-value.h"
54 #include "hphp/runtime/base/extended-logger.h"
55 #include "hphp/runtime/base/zend-math.h"
56 #include "hphp/runtime/debugger/debugger.h"
57 #include "hphp/runtime/ext/std/ext_std_output.h"
58 #include "hphp/runtime/ext/string/ext_string.h"
59 #include "hphp/runtime/ext/reflection/ext_reflection.h"
60 #include "hphp/runtime/ext/apc/ext_apc.h"
61 #include "hphp/runtime/server/cli-server.h"
62 #include "hphp/runtime/server/server-stats.h"
63 #include "hphp/runtime/vm/debug/debug.h"
64 #include "hphp/runtime/vm/jit/enter-tc.h"
65 #include "hphp/runtime/vm/jit/tc.h"
66 #include "hphp/runtime/vm/jit/translator-inline.h"
67 #include "hphp/runtime/vm/jit/translator.h"
68 #include "hphp/runtime/vm/debugger-hook.h"
69 #include "hphp/runtime/vm/event-hook.h"
70 #include "hphp/runtime/vm/runtime.h"
71 #include "hphp/runtime/vm/hh-utils.h"
72 #include "hphp/runtime/vm/unwind.h"
73 #include "hphp/runtime/vm/treadmill.h"
74 #include "hphp/runtime/vm/act-rec-defs.h"
75 #include "hphp/runtime/vm/interp-helpers.h"
78 ///////////////////////////////////////////////////////////////////////////////
79 TRACE_SET_MOD(bcinterp
);
81 THREAD_LOCAL_NO_CHECK(ExecutionContext
, g_context
);
83 ExecutionContext::ExecutionContext()
84 : m_transport(nullptr)
86 , m_implicitFlush(false)
89 , m_stdoutBytesWritten(0)
90 , m_errorState(ExecutionContext::ErrorState::NoError
)
92 , m_deferredErrors(staticEmptyVecArray())
93 , m_throwAllErrors(false)
94 , m_pageletTasksStarted(0)
96 , m_globalVarEnv(nullptr)
100 , m_unwindingCppException(false)
101 , m_lastErrorPath(staticEmptyString())
103 , m_executingSetprofileCallback(false)
104 , m_logger_hook(*this)
106 resetCoverageCounters();
107 // We don't want a new execution context to cause any request-heap
108 // allocations (because it will cause us to hold a slab, even while idle).
109 static auto s_cwd
= makeStaticString(Process::CurrentWorkingDirectory
);
111 RID().setMemoryLimit(std::to_string(RuntimeOption::RequestMemoryMaxBytes
));
112 RID().setErrorReportingLevel(RuntimeOption::RuntimeErrorReportingLevel
);
114 VariableSerializer::serializationSizeLimit
=
115 RuntimeOption::SerializationSizeLimit
;
116 tvWriteUninit(m_headerCallback
);
119 // See header for why this is required.
123 void ThreadLocalNoCheck
<ExecutionContext
>::destroy() {
125 getNoCheck()->sweep();
131 void ExecutionContext::cleanup() {
134 // Discard all units that were created via create_function().
135 for (auto& v
: m_createdFuncs
) delete v
;
136 m_createdFuncs
.clear();
139 void ExecutionContext::sweep() {
143 ExecutionContext::~ExecutionContext() {
144 // When we destroy the execution context will call destructors on any objects
145 // in the userErrorHandlers and userExceptionHandlers vectors. If these
146 // destructors call restore_*_handler() they can trigger a pop_back() on the
147 // vector resulting in double destruction. There's no reason for code to do
148 // this but we should still avoid crashing.
149 // N.B.: This is already taken care of for us if EnableObjDestructCall is on
150 if (!RuntimeOption::EnableObjDestructCall
) {
151 while (!m_userErrorHandlers
.empty()) m_userErrorHandlers
.pop_back();
152 while (!m_userExceptionHandlers
.empty()) m_userExceptionHandlers
.pop_back();
157 void ExecutionContext::backupSession() {
158 m_shutdownsBackup
= m_shutdowns
;
159 m_userErrorHandlersBackup
= m_userErrorHandlers
;
160 m_userExceptionHandlersBackup
= m_userExceptionHandlers
;
163 void ExecutionContext::restoreSession() {
164 m_shutdowns
= m_shutdownsBackup
;
165 m_userErrorHandlers
= m_userErrorHandlersBackup
;
166 m_userExceptionHandlers
= m_userExceptionHandlersBackup
;
169 ///////////////////////////////////////////////////////////////////////////////
172 String
ExecutionContext::getMimeType() const {
175 mimetype
= m_transport
->getMimeType();
178 if (strncasecmp(mimetype
.data(), "text/", 5) == 0) {
179 int pos
= mimetype
.find(';');
180 if (pos
!= String::npos
) {
181 mimetype
= mimetype
.substr(0, pos
);
183 } else if (m_transport
&& m_transport
->getUseDefaultContentType()) {
184 mimetype
= RID().getDefaultMimeType();
189 std::string
ExecutionContext::getRequestUrl(size_t szLimit
) {
190 Transport
* t
= getTransport();
191 std::string ret
= t
? t
->getUrl() : "";
192 if (szLimit
!= std::string::npos
) {
193 ret
= ret
.substr(0, szLimit
);
198 void ExecutionContext::setContentType(const String
& mimetype
,
199 const String
& charset
) {
201 String contentType
= mimetype
;
203 contentType
+= "charset=";
204 contentType
+= charset
;
205 m_transport
->addHeader("Content-Type", contentType
.c_str());
206 m_transport
->setUseDefaultContentType(false);
210 ///////////////////////////////////////////////////////////////////////////////
213 void ExecutionContext::write(const String
& s
) {
214 write(s
.data(), s
.size());
217 void ExecutionContext::setStdout(StdoutHook
* hook
) {
221 static void safe_stdout(const void *ptr
, size_t size
) {
222 write(fileno(stdout
), ptr
, size
);
225 void ExecutionContext::writeStdout(const char *s
, int len
) {
227 if (m_stdout
== nullptr) {
228 if (s_stdout_color
) {
229 safe_stdout(s_stdout_color
, strlen(s_stdout_color
));
231 safe_stdout(ANSI_COLOR_END
, strlen(ANSI_COLOR_END
));
235 m_stdoutBytesWritten
+= len
;
241 void ExecutionContext::writeTransport(const char *s
, int len
) {
243 m_transport
->sendRaw((void*)s
, len
, 200, false, true);
249 size_t ExecutionContext::getStdoutBytesWritten() const {
250 return m_stdoutBytesWritten
;
253 void ExecutionContext::write(const char *s
, int len
) {
255 m_sb
->append(s
, len
);
256 if (m_out
&& m_out
->chunk_size
> 0) {
257 if (m_sb
->size() >= m_out
->chunk_size
) {
261 if (m_implicitFlush
) flush();
263 writeTransport(s
, len
);
267 ///////////////////////////////////////////////////////////////////////////////
270 void ExecutionContext::obProtect(bool on
) {
271 m_protectedLevel
= on
? m_buffers
.size() : 0;
274 void ExecutionContext::obStart(const Variant
& handler
/* = null */,
275 int chunk_size
/* = 0 */,
276 OBFlags flags
/* = OBFlags::Default */) {
277 if (m_insideOBHandler
) {
278 raise_error("ob_start(): Cannot use output buffering "
279 "in output buffering display handlers");
281 m_buffers
.emplace_back(Variant(handler
), chunk_size
, flags
);
282 resetCurrentBuffer();
285 String
ExecutionContext::obCopyContents() {
286 if (!m_buffers
.empty()) {
287 StringBuffer
&oss
= m_buffers
.back().oss
;
292 return empty_string();
295 String
ExecutionContext::obDetachContents() {
296 if (!m_buffers
.empty()) {
297 StringBuffer
&oss
= m_buffers
.back().oss
;
302 return empty_string();
305 int ExecutionContext::obGetContentLength() {
306 if (m_buffers
.empty()) {
309 return m_buffers
.back().oss
.size();
312 void ExecutionContext::obClean(int handler_flag
) {
313 if (!m_buffers
.empty()) {
314 OutputBuffer
*last
= &m_buffers
.back();
315 if (!last
->handler
.isNull()) {
316 m_insideOBHandler
= true;
317 SCOPE_EXIT
{ m_insideOBHandler
= false; };
318 vm_call_user_func(last
->handler
,
319 make_packed_array(last
->oss
.detach(), handler_flag
));
325 bool ExecutionContext::obFlush(bool force
/*= false*/) {
326 assert(m_protectedLevel
>= 0);
328 if ((int)m_buffers
.size() <= m_protectedLevel
) {
332 auto iter
= m_buffers
.end();
333 OutputBuffer
& last
= *(--iter
);
334 if (!force
&& !(last
.flags
& OBFlags::Flushable
)) {
337 if (any(last
.flags
& OBFlags::OutputDisabled
)) {
341 const int flag
= k_PHP_OUTPUT_HANDLER_START
| k_PHP_OUTPUT_HANDLER_END
;
343 if (iter
!= m_buffers
.begin()) {
344 OutputBuffer
& prev
= *(--iter
);
345 if (last
.handler
.isNull()) {
346 prev
.oss
.absorb(last
.oss
);
348 auto str
= last
.oss
.detach();
352 m_insideOBHandler
= true;
353 SCOPE_EXIT
{ m_insideOBHandler
= false; };
354 tout
= vm_call_user_func(
355 last
.handler
, make_packed_array(str
, flag
)
358 prev
.oss
.append(tout
.toString());
360 prev
.oss
.append(str
);
367 auto str
= last
.oss
.detach();
368 if (!last
.handler
.isNull()) {
372 m_insideOBHandler
= true;
373 SCOPE_EXIT
{ m_insideOBHandler
= false; };
374 tout
= vm_call_user_func(
375 last
.handler
, make_packed_array(str
, flag
)
378 str
= tout
.toString();
380 writeTransport(str
.data(), str
.size());
385 writeTransport(str
.data(), str
.size());
389 void ExecutionContext::obFlushAll() {
395 bool ExecutionContext::obEnd() {
396 assert(m_protectedLevel
>= 0);
397 if ((int)m_buffers
.size() > m_protectedLevel
) {
398 m_buffers
.pop_back();
399 resetCurrentBuffer();
400 if (m_implicitFlush
) flush();
403 if (m_implicitFlush
) flush();
407 void ExecutionContext::obEndAll() {
411 int ExecutionContext::obGetLevel() {
412 assert((int)m_buffers
.size() >= m_protectedLevel
);
413 return m_buffers
.size() - m_protectedLevel
;
422 s_chunk_size("chunk_size"),
423 s_buffer_used("buffer_used"),
424 s_default_output_handler("default output handler");
426 Array
ExecutionContext::obGetStatus(bool full
) {
427 Array ret
= Array::Create();
429 for (auto& buffer
: m_buffers
) {
431 if (level
< m_protectedLevel
|| buffer
.handler
.isNull()) {
432 status
.set(s_name
, s_default_output_handler
);
433 status
.set(s_type
, 0);
435 status
.set(s_name
, buffer
.handler
);
436 status
.set(s_type
, 1);
440 if (any(buffer
.flags
& OBFlags::Cleanable
)) {
441 flags
|= k_PHP_OUTPUT_HANDLER_CLEANABLE
;
443 if (any(buffer
.flags
& OBFlags::Flushable
)) {
444 flags
|= k_PHP_OUTPUT_HANDLER_FLUSHABLE
;
446 if (any(buffer
.flags
& OBFlags::Removable
)) {
447 flags
|= k_PHP_OUTPUT_HANDLER_REMOVABLE
;
449 status
.set(s_flags
, flags
);
451 status
.set(s_level
, level
);
452 status
.set(s_chunk_size
, buffer
.chunk_size
);
453 status
.set(s_buffer_used
, static_cast<uint64_t>(buffer
.oss
.size()));
458 ret
= std::move(status
);
465 String
ExecutionContext::obGetBufferName() {
466 if (m_buffers
.empty()) {
468 } else if (m_buffers
.size() <= m_protectedLevel
) {
469 return s_default_output_handler
;
471 auto iter
= m_buffers
.end();
472 OutputBuffer
& buffer
= *(--iter
);
473 if (buffer
.handler
.isNull()) {
474 return s_default_output_handler
;
476 return buffer
.handler
.toString();
481 void ExecutionContext::obSetImplicitFlush(bool on
) {
482 m_implicitFlush
= on
;
485 Array
ExecutionContext::obGetHandlers() {
487 for (auto& ob
: m_buffers
) {
488 auto& handler
= ob
.handler
;
489 ret
.append(handler
.isNull() ? s_default_output_handler
: handler
);
494 void ExecutionContext::flush() {
495 if (!m_buffers
.empty() &&
496 RuntimeOption::EnableEarlyFlush
&& m_protectedLevel
&&
497 !(m_buffers
.front().flags
& OBFlags::OutputDisabled
)) {
498 OutputBuffer
&buffer
= m_buffers
.front();
499 StringBuffer
&oss
= buffer
.oss
;
501 if (any(buffer
.flags
& OBFlags::WriteToStdout
)) {
502 writeStdout(oss
.data(), oss
.size());
504 writeTransport(oss
.data(), oss
.size());
511 void ExecutionContext::resetCurrentBuffer() {
512 if (m_buffers
.empty()) {
516 m_sb
= &m_buffers
.back().oss
;
517 m_out
= &m_buffers
.back();
521 ///////////////////////////////////////////////////////////////////////////////
522 // program executions
524 void ExecutionContext::registerShutdownFunction(const Variant
& function
,
527 Array callback
= make_map_array(s_name
, function
, s_args
, arguments
);
528 Variant
& funcs
= m_shutdowns
.lvalAt(type
);
529 forceToArray(funcs
).append(callback
);
532 bool ExecutionContext::removeShutdownFunction(const Variant
& function
,
535 auto& funcs
= forceToArray(m_shutdowns
.lvalAt(type
));
536 PackedArrayInit
newFuncs(funcs
.size());
538 for (ArrayIter
iter(funcs
); iter
; ++iter
) {
539 if (!same(iter
.second().toArray()[s_name
], function
)) {
540 newFuncs
.appendWithRef(iter
.secondVal());
545 funcs
= newFuncs
.toArray();
549 bool ExecutionContext::hasShutdownFunctions(ShutdownType type
) {
550 return !m_shutdowns
.isNull() && m_shutdowns
.exists(type
) &&
551 m_shutdowns
[type
].toArray().size() >= 1;
554 Variant
ExecutionContext::pushUserErrorHandler(const Variant
& function
,
557 if (!m_userErrorHandlers
.empty()) {
558 ret
= m_userErrorHandlers
.back().first
;
560 m_userErrorHandlers
.push_back(std::pair
<Variant
,int>(function
, error_types
));
564 Variant
ExecutionContext::pushUserExceptionHandler(const Variant
& function
) {
566 if (!m_userExceptionHandlers
.empty()) {
567 ret
= m_userExceptionHandlers
.back();
569 m_userExceptionHandlers
.push_back(function
);
573 void ExecutionContext::popUserErrorHandler() {
574 if (!m_userErrorHandlers
.empty()) {
575 m_userErrorHandlers
.pop_back();
579 void ExecutionContext::clearUserErrorHandlers() {
580 while (!m_userErrorHandlers
.empty()) m_userErrorHandlers
.pop_back();
583 void ExecutionContext::popUserExceptionHandler() {
584 if (!m_userExceptionHandlers
.empty()) {
585 m_userExceptionHandlers
.pop_back();
589 void ExecutionContext::acceptRequestEventHandlers(bool enable
) {
590 m_acceptRequestEventHandlers
= enable
;
593 std::size_t ExecutionContext::registerRequestEventHandler(
594 RequestEventHandler
*handler
) {
595 assert(handler
&& handler
->getInited());
596 assert(m_acceptRequestEventHandlers
);
597 m_requestEventHandlers
.push_back(handler
);
598 return m_requestEventHandlers
.size()-1;
601 void ExecutionContext::unregisterRequestEventHandler(
602 RequestEventHandler
* handler
,
604 assert(index
< m_requestEventHandlers
.size() &&
605 m_requestEventHandlers
[index
] == handler
);
606 assert(!handler
->getInited());
607 if (index
== m_requestEventHandlers
.size()-1) {
608 m_requestEventHandlers
.pop_back();
610 m_requestEventHandlers
[index
] = nullptr;
614 static bool requestEventHandlerPriorityComp(RequestEventHandler
*a
,
615 RequestEventHandler
*b
) {
617 else if (!b
) return false;
618 else return a
->priority() < b
->priority();
621 void ExecutionContext::onRequestShutdown() {
622 while (!m_requestEventHandlers
.empty()) {
623 // handlers could cause other handlers to be registered,
624 // so need to repeat until done
625 decltype(m_requestEventHandlers
) tmp
;
626 tmp
.swap(m_requestEventHandlers
);
628 // Sort handlers by priority so that lower priority values get shutdown
630 sort(tmp
.begin(), tmp
.end(),
631 requestEventHandlerPriorityComp
);
632 for (auto* handler
: tmp
) {
633 if (!handler
) continue;
634 assert(handler
->getInited());
635 handler
->requestShutdown();
636 handler
->setInited(false);
641 void ExecutionContext::executeFunctions(ShutdownType type
) {
642 RID().resetTimer(RuntimeOption::PspTimeoutSeconds
);
643 RID().resetCPUTimer(RuntimeOption::PspCpuTimeoutSeconds
);
645 if (!m_shutdowns
.isNull() && m_shutdowns
.exists(type
)) {
647 try { m_shutdowns
.remove(type
); } catch (...) {}
649 // We mustn't destroy any callbacks until we're done with all
650 // of them. So hold them in tmp.
653 auto& var
= m_shutdowns
.lvalAt(type
);
654 if (!var
.isArray()) break;
655 auto funcs
= var
.toArray();
657 for (int pos
= 0; pos
< funcs
.size(); ++pos
) {
658 Array callback
= funcs
[pos
].toArray();
659 vm_call_user_func(callback
[s_name
], callback
[s_args
].toArray());
666 void ExecutionContext::onShutdownPreSend() {
667 // in case obStart was called without obFlush
669 try { obFlushAll(); } catch (...) {}
672 tl_heap
->resetCouldOOM(isStandardRequest());
673 executeFunctions(ShutDown
);
676 extern void ext_session_request_shutdown();
678 void ExecutionContext::onShutdownPostSend() {
679 ServerStats::SetThreadMode(ServerStats::ThreadMode::PostProcessing
);
680 tl_heap
->resetCouldOOM(isStandardRequest());
683 ServerStatsHelper
ssh("psp", ServerStatsHelper::TRACK_HWINST
);
684 executeFunctions(PostSend
);
687 bump_counter_and_rethrow(true /* isPsp */);
688 } catch (const ExitException
&e
) {
690 } catch (const Exception
&e
) {
692 } catch (const Object
&e
) {
693 onUnhandledException(e
);
697 Logger::Error("unknown exception was thrown from psp");
701 * This has to happen before requestEventHandler shutdown hooks,
702 * because it can run user code which may need to access other
703 * RequestLocal objects (such as the stream registry).
705 ext_session_request_shutdown();
707 ServerStats::SetThreadMode(ServerStats::ThreadMode::Idling
);
710 ///////////////////////////////////////////////////////////////////////////////
713 bool ExecutionContext::errorNeedsHandling(int errnum
,
714 bool callUserHandler
,
715 ErrorThrowMode mode
) {
716 if (UNLIKELY(m_throwAllErrors
)) {
717 throw Exception(folly::sformat("throwAllErrors: {}", errnum
));
719 if (mode
!= ErrorThrowMode::Never
|| errorNeedsLogging(errnum
) ||
720 RID().hasTrackErrors()) {
723 if (callUserHandler
) {
724 if (!m_userErrorHandlers
.empty() &&
725 (m_userErrorHandlers
.back().second
& errnum
) != 0) {
732 bool ExecutionContext::errorNeedsLogging(int errnum
) {
734 RID().getErrorReportingLevel() |
735 RuntimeOption::ForceErrorReportingLevel
;
736 return RuntimeOption::NoSilencer
|| (level
& errnum
) != 0;
739 struct ErrorStateHelper
{
740 ErrorStateHelper(ExecutionContext
*context
,
741 ExecutionContext::ErrorState state
) {
743 m_originalState
= m_context
->getErrorState();
744 m_context
->setErrorState(state
);
746 ~ErrorStateHelper() {
747 m_context
->setErrorState(m_originalState
);
750 ExecutionContext
*m_context
;
751 ExecutionContext::ErrorState m_originalState
;
757 s_function("function"),
759 s_php_errormsg("php_errormsg"),
760 s_error_num("error-num"),
761 s_error_string("error-string"),
762 s_error_file("error-file"),
763 s_error_line("error-line"),
764 s_error_backtrace("error-backtrace"),
765 s_overflow("overflow");
767 void ExecutionContext::handleError(const std::string
& msg
,
769 bool callUserHandler
,
771 const std::string
& prefix
,
772 bool skipFrame
/* = false */) {
773 SYNC_VM_REGS_SCOPED();
775 auto newErrorState
= ErrorState::ErrorRaised
;
776 switch (getErrorState()) {
777 case ErrorState::ErrorRaised
:
778 case ErrorState::ErrorRaisedByUserHandler
:
780 case ErrorState::ExecutingUserHandler
:
781 newErrorState
= ErrorState::ErrorRaisedByUserHandler
;
787 // Potentially upgrade the error to E_USER_ERROR
788 if (errnum
& RuntimeOption::ErrorUpgradeLevel
&
789 static_cast<int>(ErrorMode::UPGRADEABLE_ERROR
)) {
790 errnum
= static_cast<int>(ErrorMode::USER_ERROR
);
791 mode
= ErrorThrowMode::IfUnhandled
;
794 auto const ee
= skipFrame
?
795 ExtendedException(ExtendedException::SkipFrame
{}, msg
) :
796 ExtendedException(msg
);
797 bool handled
= false;
799 ErrorStateHelper
esh(this, newErrorState
);
800 if (callUserHandler
) {
801 handled
= callUserErrorHandler(ee
, errnum
, false);
805 recordLastError(ee
, errnum
);
808 if (g_system_profiler
) {
809 g_system_profiler
->errorCallBack(ee
, errnum
, msg
);
813 if (mode
== ErrorThrowMode::Always
||
814 (mode
== ErrorThrowMode::IfUnhandled
&& !handled
)) {
815 DEBUGGER_ATTACHED_ONLY(phpDebuggerErrorHook(ee
, errnum
, msg
));
817 errnum
== static_cast<int>(ErrorMode::RECOVERABLE_ERROR
);
818 raise_fatal_error(msg
.c_str(), ee
.getBacktrace(), isRecoverable
,
819 !errorNeedsLogging(errnum
) /* silent */);
826 if (RID().hasTrackErrors() && fp
) {
827 // Set $php_errormsg in the parent scope
828 Variant
msg(ee
.getMessage());
829 if (fp
->func()->isBuiltin()) {
830 fp
= getPrevVMState(fp
);
833 auto id
= fp
->func()->lookupVarId(s_php_errormsg
.get());
834 if (id
!= kInvalidId
) {
835 auto local
= frame_local(fp
, id
);
836 tvSet(*msg
.asTypedValue(), *tvToCell(local
));
837 } else if ((fp
->func()->attrs() & AttrMayUseVV
) && fp
->hasVarEnv()) {
838 fp
->getVarEnv()->set(s_php_errormsg
.get(), msg
.asTypedValue());
842 // If we're inside an error handler already, queue it up on the deferred
844 if (getErrorState() == ErrorState::ExecutingUserHandler
) {
845 auto& deferred
= m_deferredErrors
;
846 if (deferred
.size() < RuntimeOption::EvalMaxDeferredErrors
) {
847 auto fileAndLine
= ee
.getFileAndLine();
852 s_error_file
, std::move(fileAndLine
.first
),
853 s_error_line
, fileAndLine
.second
,
854 s_error_backtrace
, ee
.getBacktrace()
857 } else if (!deferred
.empty()) {
858 auto& last
= deferred
.lvalAt(int64_t{deferred
.size() - 1});
859 if (last
.isDict()) last
.asArrRef().set(s_overflow
, true);
863 if (errorNeedsLogging(errnum
)) {
864 DEBUGGER_ATTACHED_ONLY(phpDebuggerErrorHook(ee
, errnum
, ee
.getMessage()));
865 auto fileAndLine
= ee
.getFileAndLine();
866 Logger::Log(Logger::LogError
, prefix
.c_str(), ee
,
867 fileAndLine
.first
.c_str(), fileAndLine
.second
);
872 bool ExecutionContext::callUserErrorHandler(const Exception
&e
, int errnum
,
873 bool swallowExceptions
) {
874 switch (getErrorState()) {
875 case ErrorState::ExecutingUserHandler
:
876 case ErrorState::ErrorRaisedByUserHandler
:
881 if (!m_userErrorHandlers
.empty() &&
882 (m_userErrorHandlers
.back().second
& errnum
) != 0) {
883 auto fileAndLine
= std::make_pair(empty_string(), 0);
885 if (auto const ee
= dynamic_cast<const ExtendedException
*>(&e
)) {
886 fileAndLine
= ee
->getFileAndLine();
887 backtrace
= ee
->getBacktrace();
890 ErrorStateHelper
esh(this, ErrorState::ExecutingUserHandler
);
891 auto const ar
= g_context
->getFrameAtDepth(0);
892 auto const context
= RuntimeOption::EnableContextInErrorHandler
893 ? getDefinedVariables(ar
)
895 m_deferredErrors
= Array::CreateVec();
896 SCOPE_EXIT
{ m_deferredErrors
= Array::CreateVec(); };
897 if (!same(vm_call_user_func
898 (m_userErrorHandlers
.back().first
,
899 make_packed_array(errnum
, String(e
.getMessage()),
900 fileAndLine
.first
, fileAndLine
.second
, context
,
905 } catch (const RequestTimeoutException
&) {
906 static auto requestErrorHandlerTimeoutCounter
=
907 ServiceData::createTimeSeries("requests_timed_out_error_handler",
908 {ServiceData::StatsType::COUNT
});
909 requestErrorHandlerTimeoutCounter
->addValue(1);
910 ServerStats::Log("request.timed_out.error_handler", 1);
912 if (!swallowExceptions
) throw;
913 } catch (const RequestCPUTimeoutException
&) {
914 static auto requestErrorHandlerCPUTimeoutCounter
=
915 ServiceData::createTimeSeries("requests_cpu_timed_out_error_handler",
916 {ServiceData::StatsType::COUNT
});
917 requestErrorHandlerCPUTimeoutCounter
->addValue(1);
918 ServerStats::Log("request.cpu_timed_out.error_handler", 1);
920 if (!swallowExceptions
) throw;
921 } catch (const RequestMemoryExceededException
&) {
922 static auto requestErrorHandlerMemoryExceededCounter
=
923 ServiceData::createTimeSeries(
924 "requests_memory_exceeded_error_handler",
925 {ServiceData::StatsType::COUNT
});
926 requestErrorHandlerMemoryExceededCounter
->addValue(1);
927 ServerStats::Log("request.memory_exceeded.error_handler", 1);
929 if (!swallowExceptions
) throw;
931 static auto requestErrorHandlerOtherExceptionCounter
=
932 ServiceData::createTimeSeries(
933 "requests_other_exception_error_handler",
934 {ServiceData::StatsType::COUNT
});
935 requestErrorHandlerOtherExceptionCounter
->addValue(1);
936 ServerStats::Log("request.other_exception.error_handler", 1);
938 if (!swallowExceptions
) throw;
944 bool ExecutionContext::onFatalError(const Exception
&e
) {
945 tl_heap
->resetCouldOOM(isStandardRequest());
947 // need to restore the error reporting level, because the fault
948 // handler for silencers won't be run on fatals, and we might be
949 // about to run a user error handler (and psp/shutdown code).
950 RID().setErrorReportingLevel(RuntimeOption::RuntimeErrorReportingLevel
);
952 auto prefix
= "\nFatal error: ";
953 auto errnum
= static_cast<int>(ErrorMode::FATAL_ERROR
);
954 auto const fatal
= dynamic_cast<const FatalErrorException
*>(&e
);
955 if (fatal
&& fatal
->isRecoverable()) {
956 prefix
= "\nCatchable fatal error: ";
957 errnum
= static_cast<int>(ErrorMode::RECOVERABLE_ERROR
);
960 recordLastError(e
, errnum
);
962 bool silenced
= false;
963 auto fileAndLine
= std::make_pair(empty_string(), 0);
964 if (auto const ee
= dynamic_cast<const ExtendedException
*>(&e
)) {
965 silenced
= ee
->isSilent();
966 fileAndLine
= ee
->getFileAndLine();
968 // need to silence even with the AlwaysLogUnhandledExceptions flag set
969 if (!silenced
&& RuntimeOption::AlwaysLogUnhandledExceptions
) {
970 Logger::Log(Logger::LogError
, prefix
, e
, fileAndLine
.first
.c_str(),
973 bool handled
= false;
974 if (RuntimeOption::CallUserHandlerOnFatals
) {
975 handled
= callUserErrorHandler(e
, errnum
, true);
977 if (!handled
&& !silenced
&& !RuntimeOption::AlwaysLogUnhandledExceptions
) {
978 Logger::Log(Logger::LogError
, prefix
, e
, fileAndLine
.first
.c_str(),
984 bool ExecutionContext::onUnhandledException(Object e
) {
985 String err
= e
.toString();
986 if (RuntimeOption::AlwaysLogUnhandledExceptions
) {
987 Logger::Error("\nFatal error: Uncaught %s", err
.data());
990 if (e
.instanceof(SystemLib::s_ThrowableClass
)) {
991 // user thrown exception
992 if (!m_userExceptionHandlers
.empty()) {
993 if (!same(vm_call_user_func
994 (m_userExceptionHandlers
.back(),
995 make_packed_array(e
)),
1005 if (!RuntimeOption::AlwaysLogUnhandledExceptions
) {
1006 Logger::Error("\nFatal error: Uncaught %s", err
.data());
1011 ///////////////////////////////////////////////////////////////////////////////
1013 void ExecutionContext::debuggerInfo(
1014 std::vector
<std::pair
<const char*,std::string
>>& info
) {
1015 int64_t newInt
= convert_bytes_to_long(IniSetting::Get("memory_limit"));
1017 newInt
= std::numeric_limits
<int64_t>::max();
1019 if (newInt
== std::numeric_limits
<int64_t>::max()) {
1020 info
.emplace_back("Max Memory", "(unlimited)");
1022 info
.emplace_back("Max Memory", IDebuggable::FormatSize(newInt
));
1024 info
.emplace_back("Max Time",
1025 IDebuggable::FormatTime(RID().getTimeout() * 1000));
1028 void ExecutionContext::setenv(const String
& name
, const String
& value
) {
1029 m_envs
.set(name
, value
);
1032 void ExecutionContext::unsetenv(const String
& name
) {
1033 m_envs
.remove(name
);
1036 String
ExecutionContext::getenv(const String
& name
) const {
1037 if (m_envs
.exists(name
)) {
1038 return m_envs
[name
].toString();
1040 if (is_cli_mode()) {
1041 auto envs
= cli_env();
1042 if (envs
.exists(name
)) return envs
[name
].toString();
1045 if (auto value
= ::getenv(name
.data())) {
1046 return String(value
, CopyString
);
1048 if (RuntimeOption::EnvVariables
.find(name
.c_str()) != RuntimeOption::EnvVariables
.end()) {
1049 return String(RuntimeOption::EnvVariables
[name
.c_str()].data(), CopyString
);
1054 Cell
ExecutionContext::lookupClsCns(const NamedEntity
* ne
,
1055 const StringData
* cls
,
1056 const StringData
* cns
) {
1057 Class
* class_
= nullptr;
1059 class_
= Unit::loadClass(ne
, cls
);
1060 } catch (Object
& ex
) {
1061 // For compatibility with php, throwing through a constant lookup has
1062 // different behavior inside a property initializer (86pinit/86sinit).
1063 auto ar
= getStackFrame();
1064 if (ar
&& ar
->func() && Func::isSpecial(ar
->func()->name())) {
1065 raise_warning("Uncaught %s", ex
.toString().data());
1066 raise_error("Couldn't find constant %s::%s", cls
->data(), cns
->data());
1070 if (class_
== nullptr) {
1071 raise_error(Strings::UNKNOWN_CLASS
, cls
->data());
1073 Cell clsCns
= class_
->clsCnsGet(cns
);
1074 if (clsCns
.m_type
== KindOfUninit
) {
1075 raise_error("Couldn't find constant %s::%s", cls
->data(), cns
->data());
1080 static Class
* loadClass(StringData
* clsName
) {
1081 Class
* class_
= Unit::loadClass(clsName
);
1082 if (class_
== nullptr) {
1083 raise_error(Strings::UNKNOWN_CLASS
, clsName
->data());
1088 ObjectData
* ExecutionContext::createObject(StringData
* clsName
,
1089 const Variant
& params
,
1090 bool init
/* = true */) {
1091 return createObject(loadClass(clsName
), params
, init
);
1094 ObjectData
* ExecutionContext::createObject(const Class
* class_
,
1095 const Variant
& params
,
1097 auto o
= Object::attach(newInstance(const_cast<Class
*>(class_
)));
1099 initObject(class_
, params
, o
.get());
1105 ObjectData
* ExecutionContext::createObjectOnly(StringData
* clsName
) {
1106 return createObject(clsName
, init_null_variant
, false);
1109 ObjectData
* ExecutionContext::initObject(StringData
* clsName
,
1110 const Variant
& params
,
1112 return initObject(loadClass(clsName
), params
, o
);
1115 ObjectData
* ExecutionContext::initObject(const Class
* class_
,
1116 const Variant
& params
,
1118 auto ctor
= class_
->getCtor();
1119 if (!(ctor
->attrs() & AttrPublic
)) {
1120 std::string msg
= "Access to non-public constructor of class ";
1121 msg
+= class_
->name()->data();
1122 Reflection::ThrowReflectionExceptionObject(msg
);
1125 if (!isContainerOrNull(params
)) {
1126 throw_param_is_not_container();
1128 tvDecRefGen(invokeFunc(ctor
, params
, o
));
1132 ActRec
* ExecutionContext::getStackFrame() {
1137 ObjectData
* ExecutionContext::getThis() {
1139 ActRec
* fp
= vmfp();
1140 if (fp
->skipFrame()) fp
= getPrevVMStateSkipFrame(fp
);
1141 if (fp
&& fp
->func()->cls() && fp
->hasThis()) {
1142 return fp
->getThis();
1147 Class
* ExecutionContext::getContextClass() {
1149 ActRec
* ar
= vmfp();
1150 assert(ar
!= nullptr);
1151 if (ar
->skipFrame()) ar
= getPrevVMStateSkipFrame(ar
);
1152 return ar
? ar
->m_func
->cls() : nullptr;
1155 Class
* ExecutionContext::getParentContextClass() {
1156 if (Class
* ctx
= getContextClass()) {
1157 return ctx
->parent();
1162 StringData
* ExecutionContext::getContainingFileName() {
1164 ActRec
* ar
= vmfp();
1165 if (ar
== nullptr) return staticEmptyString();
1166 if (ar
->skipFrame()) ar
= getPrevVMStateSkipFrame(ar
);
1167 if (ar
== nullptr) return staticEmptyString();
1168 Unit
* unit
= ar
->m_func
->unit();
1169 assert(unit
->filepath()->isStatic());
1170 // XXX: const StringData* -> Variant(bool) conversion problem makes this ugly
1171 return const_cast<StringData
*>(unit
->filepath());
1174 int ExecutionContext::getLine() {
1176 ActRec
* ar
= vmfp();
1177 Unit
* unit
= ar
? ar
->m_func
->unit() : nullptr;
1178 Offset pc
= unit
? pcOff() : 0;
1179 if (ar
== nullptr) return -1;
1180 if (ar
->skipFrame()) ar
= getPrevVMStateSkipFrame(ar
, &pc
);
1181 if (ar
== nullptr || (unit
= ar
->m_func
->unit()) == nullptr) return -1;
1182 return unit
->getLineNumber(pc
);
1185 const StaticString
s___call("__call");
1186 const StaticString
s___callStatic("__callStatic");
1187 const StaticString
s_call_user_func("call_user_func");
1188 const StaticString
s_call_user_func_array("call_user_func_array");
1190 Array
ExecutionContext::getCallerInfo() {
1193 if (ar
->skipFrame()) {
1194 ar
= getPrevVMStateSkipFrame(ar
);
1195 if (!ar
) return empty_array();
1197 while (ar
->func()->name()->isame(s_call_user_func
.get())
1198 || ar
->func()->name()->isame(s_call_user_func_array
.get())) {
1199 ar
= getPrevVMState(ar
);
1200 if (ar
== nullptr) {
1201 return empty_array();
1206 ar
= getPrevVMState(ar
, &pc
);
1207 while (ar
!= nullptr) {
1208 if (!ar
->func()->name()->isame(s_call_user_func
.get())
1209 && !ar
->func()->name()->isame(s_call_user_func_array
.get())) {
1210 auto const unit
= ar
->func()->unit();
1212 if ((lineNumber
= unit
->getLineNumber(pc
)) != -1) {
1213 auto const cls
= ar
->func()->cls();
1214 if (cls
!= nullptr && !ar
->func()->isClosureBody()) {
1215 return make_map_array(
1216 s_class
, const_cast<StringData
*>(cls
->name()),
1217 s_file
, const_cast<StringData
*>(unit
->filepath()),
1218 s_function
, const_cast<StringData
*>(ar
->func()->name()),
1222 return make_map_array(
1223 s_file
, const_cast<StringData
*>(unit
->filepath()),
1224 s_function
, const_cast<StringData
*>(ar
->func()->name()),
1230 ar
= getPrevVMState(ar
, &pc
);
1232 return empty_array();
1235 ActRec
* ExecutionContext::getFrameAtDepth(int frame
) {
1238 if (UNLIKELY(!fp
)) return nullptr;
1239 auto pc
= fp
->func()->unit()->offsetOf(vmpc());
1241 fp
= getPrevVMState(fp
, &pc
);
1242 if (UNLIKELY(!fp
)) return nullptr;
1243 if (UNLIKELY(fp
->skipFrame())) continue;
1246 while (fp
->skipFrame()) {
1247 fp
= getPrevVMState(fp
, &pc
);
1248 if (UNLIKELY(!fp
)) return nullptr;
1250 if (UNLIKELY(fp
->localsDecRefd())) return nullptr;
1251 auto const curOp
= fp
->func()->unit()->getOp(pc
);
1252 if (UNLIKELY(curOp
== Op::RetC
|| curOp
== Op::RetV
||
1253 curOp
== Op::CreateCont
|| curOp
== Op::Await
)) {
1256 assert(!fp
->magicDispatch());
1260 VarEnv
* ExecutionContext::getOrCreateVarEnv(int frame
) {
1261 auto const fp
= getFrameAtDepth(frame
);
1262 if (!fp
|| !(fp
->func()->attrs() & AttrMayUseVV
)) {
1263 raise_error("Could not create variable environment");
1265 if (!fp
->hasVarEnv()) {
1266 fp
->setVarEnv(VarEnv::createLocal(fp
));
1268 return fp
->getVarEnv();
1271 void ExecutionContext::setVar(StringData
* name
, const TypedValue
* v
) {
1273 ActRec
*fp
= vmfp();
1275 if (fp
->skipFrame()) fp
= getPrevVMStateSkipFrame(fp
);
1276 if (fp
) fp
->getVarEnv()->set(name
, v
);
1279 void ExecutionContext::bindVar(StringData
* name
, TypedValue
* v
) {
1281 ActRec
*fp
= vmfp();
1283 if (fp
->skipFrame()) fp
= getPrevVMStateSkipFrame(fp
);
1284 if (fp
) fp
->getVarEnv()->bind(name
, v
);
1287 Array
ExecutionContext::getLocalDefinedVariables(int frame
) {
1289 ActRec
*fp
= vmfp();
1290 for (; frame
> 0; --frame
) {
1292 fp
= getPrevVMState(fp
);
1294 return getDefinedVariables(fp
);
1297 bool ExecutionContext::setHeaderCallback(const Variant
& callback
) {
1298 if (cellAsVariant(g_context
->m_headerCallback
).toBoolean()) {
1299 // return false if a callback has already been set.
1302 cellAsVariant(g_context
->m_headerCallback
) = callback
;
1306 TypedValue
ExecutionContext::invokeUnit(const Unit
* unit
) {
1307 checkHHConfig(unit
);
1309 auto const func
= unit
->getMain(nullptr);
1310 return invokeFunc(func
, init_null_variant
, nullptr, nullptr,
1311 m_globalVarEnv
, nullptr, InvokePseudoMain
,
1312 !unit
->useStrictTypes());
1315 void ExecutionContext::syncGdbState() {
1316 if (RuntimeOption::EvalJit
&& !RuntimeOption::EvalJitNoGdb
) {
1317 Debug::DebugInfo::Get()->debugSync();
1321 void ExecutionContext::pushVMState(Cell
* savedSP
) {
1322 if (UNLIKELY(!vmfp())) {
1324 assert(m_nestedVMs
.size() == 0);
1328 TRACE(3, "savedVM: %p %p %p %p\n", vmpc(), vmfp(), vmFirstAR(), savedSP
);
1329 auto& savedVM
= m_nestedVMs
.alloc_back();
1330 savedVM
.pc
= vmpc();
1331 savedVM
.fp
= vmfp();
1332 savedVM
.firstAR
= vmFirstAR();
1333 savedVM
.sp
= savedSP
;
1334 savedVM
.mInstrState
= vmMInstrState();
1335 savedVM
.jitCalledFrame
= vmJitCalledFrame();
1338 if (debug
&& savedVM
.fp
&&
1339 savedVM
.fp
->m_func
&&
1340 savedVM
.fp
->m_func
->unit()) {
1341 // Some asserts and tracing.
1342 const Func
* func
= savedVM
.fp
->m_func
;
1343 /* bound-check asserts in offsetOf */
1344 func
->unit()->offsetOf(savedVM
.pc
);
1345 TRACE(3, "pushVMState: saving frame %s pc %p off %d fp %p\n",
1346 func
->name()->data(),
1348 func
->unit()->offsetOf(savedVM
.pc
),
1353 void ExecutionContext::popVMState() {
1354 if (UNLIKELY(m_nestedVMs
.empty())) {
1358 vmFirstAR() = nullptr;
1362 assert(m_nestedVMs
.size() >= 1);
1364 VMState
&savedVM
= m_nestedVMs
.back();
1365 vmpc() = savedVM
.pc
;
1366 vmfp() = savedVM
.fp
;
1367 vmFirstAR() = savedVM
.firstAR
;
1368 vmStack().top() = savedVM
.sp
;
1369 vmMInstrState() = savedVM
.mInstrState
;
1370 vmJitCalledFrame() = savedVM
.jitCalledFrame
;
1374 savedVM
.fp
->m_func
&&
1375 savedVM
.fp
->m_func
->unit()) {
1376 const Func
* func
= savedVM
.fp
->m_func
;
1377 (void) /* bound-check asserts in offsetOf */
1378 func
->unit()->offsetOf(savedVM
.pc
);
1379 TRACE(3, "popVMState: restoring frame %s pc %p off %d fp %p\n",
1380 func
->name()->data(),
1382 func
->unit()->offsetOf(savedVM
.pc
),
1387 m_nestedVMs
.pop_back();
1390 TRACE(1, "Reentry: exit fp %p pc %p\n", vmfp(), vmpc());
1393 void ExecutionContext::ExcLoggerHook::operator()(
1394 const char* header
, const char* msg
, const char* ending
1403 s_php_namespace("<?php namespace "),
1404 s_hh_namespace("<?hh namespace "),
1405 s_curly_return(" { return "),
1406 s_semicolon_curly("; }"),
1407 s_php_return("<?php return "),
1408 s_hh_return("<?hh return "),
1410 s_stdclass("stdclass");
1412 void ExecutionContext::requestInit() {
1413 assert(SystemLib::s_unit
);
1416 VarEnv::createGlobal();
1417 vmStack().requestInit();
1418 ObjectData::resetMaxId();
1419 ResourceHdr::resetMaxId();
1420 jit::tc::requestInit();
1422 if (RuntimeOption::EvalJitEnableRenameFunction
) {
1423 assert(SystemLib::s_anyNonPersistentBuiltins
);
1427 * The normal case for production mode is that all builtins are
1428 * persistent, and every systemlib unit is accordingly going to be
1431 * However, if we have rename_function generally enabled, or if any
1432 * builtin functions were specified as interceptable at
1433 * repo-generation time, we'll actually need to merge systemlib on
1434 * every request because some of the builtins will not be marked
1437 if (UNLIKELY(SystemLib::s_anyNonPersistentBuiltins
)) {
1438 SystemLib::s_unit
->merge();
1439 SystemLib::mergePersistentUnits();
1440 if (SystemLib::s_hhas_unit
) SystemLib::s_hhas_unit
->merge();
1441 if (SystemLib::s_nativeFuncUnit
) SystemLib::s_nativeFuncUnit
->merge();
1442 if (SystemLib::s_nativeClassUnit
) SystemLib::s_nativeClassUnit
->merge();
1444 // System units are merge only, and everything is persistent.
1445 assert(SystemLib::s_unit
->isEmpty());
1446 assert(!SystemLib::s_hhas_unit
|| SystemLib::s_hhas_unit
->isEmpty());
1447 assert(!SystemLib::s_nativeFuncUnit
||
1448 SystemLib::s_nativeFuncUnit
->isEmpty());
1449 assert(!SystemLib::s_nativeClassUnit
||
1450 SystemLib::s_nativeClassUnit
->isEmpty());
1453 profileRequestStart();
1455 HHProf::Request::StartProfiling();
1458 Class
* cls
= NamedEntity::get(s_stdclass
.get())->clsList();
1460 assert(cls
== SystemLib::s_stdclassClass
);
1463 if (Logger::UseRequestLog
) Logger::SetThreadHook(&m_logger_hook
);
1465 // Needs to be last (or nearly last): might cause unit merging to call an
1466 // extension function in the VM; this is bad if systemlib itself hasn't been
1468 autoTypecheckRequestInit();
1471 void ExecutionContext::requestExit() {
1472 autoTypecheckRequestExit();
1473 HHProf::Request::FinishProfiling();
1477 vmStack().requestExit();
1478 profileRequestEnd();
1479 EventHook::Disable();
1482 MIterTable::clear();
1484 if (m_globalVarEnv
) {
1485 req::destroy_raw(m_globalVarEnv
);
1486 m_globalVarEnv
= nullptr;
1489 if (!m_lastError
.isNull()) {
1493 m_deferredErrors
= Array::CreateVec();
1495 if (Logger::UseRequestLog
) Logger::SetThreadHook(nullptr);
1499 * Shared implementation for invokeFunc{,Few}().
1501 * The `doCheckStack' and `doInitArgs' callbacks should return truthy in order
1502 * to short-circuit the rest of invokeFuncImpl() and return early, else they
1503 * should return falsey.
1505 * The `doInitArgs' and `doEnterVM' callbacks take an ActRec* argument
1506 * corresponding to the reentry frame.
1508 template<class FStackCheck
, class FInitArgs
, class FEnterVM
>
1510 TypedValue
ExecutionContext::invokeFuncImpl(const Func
* f
,
1511 ObjectData
* thiz
, Class
* cls
,
1512 uint32_t argc
, StringData
* invName
,
1514 FStackCheck doStackCheck
,
1515 FInitArgs doInitArgs
,
1516 FEnterVM doEnterVM
) {
1518 // If `f' is a regular function, `thiz' and `cls' must be null.
1519 assert(IMPLIES(!f
->preClass(), f
->isPseudoMain() || (!thiz
&& !cls
)));
1520 // If `f' is a method, either `thiz' or `cls' must be non-null.
1521 assert(IMPLIES(f
->preClass(), thiz
|| cls
));
1522 // If `f' is a static method, thiz must be null.
1523 assert(IMPLIES(f
->isStaticInPrologue(), !thiz
));
1524 // invName should only be non-null if we are calling __call or __callStatic.
1525 assert(IMPLIES(invName
, f
->name()->isame(s___call
.get()) ||
1526 f
->name()->isame(s___callStatic
.get())));
1529 auto const reentrySP
= vmStack().top();
1531 if (thiz
!= nullptr) thiz
->incRefCount();
1534 if (doStackCheck(retval
)) return retval
;
1536 ActRec
* ar
= vmStack().allocA();
1537 ar
->setReturnVMExit();
1546 ar
->initNumArgs(argc
);
1548 if (UNLIKELY(invName
!= nullptr)) {
1549 ar
->setMagicDispatch(invName
);
1555 if (vmfp() == nullptr) {
1556 TRACE(1, "Reentry: enter %s(%p) from top-level\n",
1557 f
->name()->data(), ar
);
1559 TRACE(1, "Reentry: enter %s(pc %p ar %p) from %s(%p)\n",
1560 f
->name()->data(), vmpc(), ar
,
1561 vmfp()->m_func
? vmfp()->m_func
->name()->data()
1567 if (doInitArgs(ar
, retval
)) return retval
;
1570 ar
->setUseWeakTypes();
1572 setTypesFlag(vmfp(), ar
);
1576 pushVMState(reentrySP
);
1579 vmStack().top() == reentrySP
,
1580 "vmsp() mismatch around reentry: before @ {}, after @ {}",
1581 reentrySP
, vmStack().top()
1588 // `retptr' might point somewhere that is affected by {push,pop}VMState(),
1589 // so don't write to it until after we pop the nested VM state.
1590 tvCopy(*vmStack().topTV(), retval
);
1591 vmStack().discard();
1597 * Enter VM by calling action(), which invokes a function or resumes
1598 * an async function. The 'ar' argument points to an ActRec of the
1599 * invoked/resumed function.
1601 template<class Action
>
1602 static inline void enterVMCustomHandler(ActRec
* ar
, Action action
) {
1605 assert(isReturnHelper(reinterpret_cast<void*>(ar
->m_savedRip
)));
1606 assert(ar
->m_soff
== 0);
1608 auto ec
= &*g_context
;
1609 DEBUG_ONLY
int faultDepth
= ec
->m_faults
.size();
1610 SCOPE_EXIT
{ assert(ec
->m_faults
.size() == faultDepth
); };
1613 vmJitCalledFrame() = nullptr;
1618 exception_handler(enterVMAtCurPC
);
1622 template<class Action
>
1623 static inline void enterVM(ActRec
* ar
, Action action
) {
1624 enterVMCustomHandler(ar
, [&] { exception_handler(action
); });
1627 TypedValue
ExecutionContext::invokeFunc(const Func
* f
,
1628 const Variant
& args_
,
1629 ObjectData
* thiz
/* = NULL */,
1630 Class
* cls
/* = NULL */,
1631 VarEnv
* varEnv
/* = NULL */,
1632 StringData
* invName
/* = NULL */,
1633 InvokeFlags flags
/* = InvokeNormal */,
1634 bool useWeakTypes
/* = false */) {
1635 const auto& args
= *args_
.asCell();
1636 assert(isContainerOrNull(args
));
1638 auto const argc
= cellIsNull(&args
) ? 0 : getContainerSize(args
);
1639 // If we are inheriting a variable environment, then `args' must be empty.
1640 assert(IMPLIES(varEnv
, argc
== 0));
1642 auto const doCheckStack
= [&](TypedValue
& retval
) {
1643 // We must do a stack overflow check for leaf functions on re-entry,
1644 // because we won't have checked that the stack is deep enough for a
1645 // leaf function /after/ re-entry, and the prologue for the leaf
1646 // function will not make a check.
1647 if (f
->attrs() & AttrPhpLeafFn
||
1648 !(f
->numParams() + kNumActRecCells
<= kStackCheckReenterPadding
)) {
1649 // Check both the native stack and VM stack for overflow.
1650 checkStack(vmStack(), f
,
1651 kNumActRecCells
/* numParams is included in f->maxStackCells */);
1653 // invokeFunc() must always check the native stack for overflow no
1658 // Handle includes of pseudomains.
1659 if (flags
& InvokePseudoMain
) {
1660 assert(f
->isPseudoMain());
1661 assert(cellIsNull(&args
) || !getContainerSize(args
));
1663 auto toMerge
= f
->unit();
1665 if (toMerge
->isMergeOnly()) {
1666 retval
= *toMerge
->getMainReturn();
1673 auto const doInitArgs
= [&] (ActRec
* ar
, TypedValue
& retval
) {
1675 auto const& prepArgs
= cellIsNull(&args
)
1676 ? make_tv
<KindOfArray
>(staticEmptyArray())
1678 auto prepResult
= prepareArrayArgs(ar
, prepArgs
, vmStack(), 0,
1679 flags
& InvokeCuf
, &retval
);
1680 if (UNLIKELY(!prepResult
)) {
1681 assert(KindOfNull
== retval
.m_type
);
1688 auto const doEnterVM
= [&] (ActRec
* ar
) {
1692 varEnv
? StackArgsState::Untrimmed
: StackArgsState::Trimmed
,
1698 return invokeFuncImpl(f
, thiz
, cls
, argc
, invName
, useWeakTypes
,
1699 doCheckStack
, doInitArgs
, doEnterVM
);
1702 TypedValue
ExecutionContext::invokeFuncFew(const Func
* f
,
1704 StringData
* invName
,
1706 const TypedValue
* argv
,
1707 bool useWeakTypes
/* = false */) {
1708 auto const doCheckStack
= [&](TypedValue
&) {
1709 // See comments in invokeFunc().
1710 if (f
->attrs() & AttrPhpLeafFn
||
1711 !(argc
+ kNumActRecCells
<= kStackCheckReenterPadding
)) {
1712 checkStack(vmStack(), f
, argc
+ kNumActRecCells
);
1719 auto const doInitArgs
= [&](ActRec
* /*ar*/, TypedValue
&) {
1720 for (ssize_t i
= 0; i
< argc
; ++i
) {
1721 const TypedValue
*from
= &argv
[i
];
1722 TypedValue
*to
= vmStack().allocTV();
1723 if (LIKELY(from
->m_type
!= KindOfRef
|| !f
->byRef(i
))) {
1724 cellDup(*tvToCell(from
), *to
);
1732 auto const doEnterVM
= [&] (ActRec
* ar
) {
1733 enterVM(ar
, [&] { enterVMAtFunc(ar
, StackArgsState::Untrimmed
, nullptr); });
1736 return invokeFuncImpl(f
,
1737 ActRec::decodeThis(thisOrCls
),
1738 ActRec::decodeClass(thisOrCls
),
1739 argc
, invName
, useWeakTypes
,
1740 doCheckStack
, doInitArgs
, doEnterVM
);
1743 static void prepareAsyncFuncEntry(ActRec
* enterFnAr
, Resumable
* resumable
) {
1745 assert(enterFnAr
->func()->isAsync());
1746 assert(enterFnAr
->resumed());
1750 vmpc() = vmfp()->func()->unit()->at(resumable
->resumeOffset());
1751 assert(vmfp()->func()->contains(vmpc()));
1752 EventHook::FunctionResumeAwait(enterFnAr
);
1755 void ExecutionContext::resumeAsyncFunc(Resumable
* resumable
,
1756 ObjectData
* freeObj
,
1757 const Cell awaitResult
) {
1758 assert(tl_regState
== VMRegState::CLEAN
);
1759 SCOPE_EXIT
{ assert(tl_regState
== VMRegState::CLEAN
); };
1761 auto fp
= resumable
->actRec();
1762 // We don't need to check for space for the ActRec (unlike generally
1763 // in normal re-entry), because the ActRec isn't on the stack.
1764 checkStack(vmStack(), fp
->func(), 0);
1766 Cell
* savedSP
= vmStack().top();
1767 cellDup(awaitResult
, *vmStack().allocC());
1769 // decref after awaitResult is on the stack
1772 pushVMState(savedSP
);
1773 SCOPE_EXIT
{ popVMState(); };
1776 prepareAsyncFuncEntry(fp
, resumable
);
1778 const bool useJit
= RID().getJit();
1779 if (LIKELY(useJit
&& resumable
->resumeAddr())) {
1780 Stats::inc(Stats::VMEnter
);
1781 jit::enterTCAfterPrologue(resumable
->resumeAddr());
1788 void ExecutionContext::resumeAsyncFuncThrow(Resumable
* resumable
,
1789 ObjectData
* freeObj
,
1790 ObjectData
* exception
) {
1792 assert(exception
->instanceof(SystemLib::s_ThrowableClass
));
1793 assert(tl_regState
== VMRegState::CLEAN
);
1794 SCOPE_EXIT
{ assert(tl_regState
== VMRegState::CLEAN
); };
1796 auto fp
= resumable
->actRec();
1797 checkStack(vmStack(), fp
->func(), 0);
1799 // decref after we hold reference to the exception
1800 Object
e(exception
);
1803 pushVMState(vmStack().top());
1804 SCOPE_EXIT
{ popVMState(); };
1806 enterVMCustomHandler(fp
, [&] {
1807 prepareAsyncFuncEntry(fp
, resumable
);
1809 unwindPhp(exception
);
1813 ActRec
* ExecutionContext::getPrevVMState(const ActRec
* fp
,
1814 Offset
* prevPc
/* = NULL */,
1815 TypedValue
** prevSp
/* = NULL */,
1816 bool* fromVMEntry
/* = NULL */) {
1817 if (fp
== nullptr) {
1820 ActRec
* prevFp
= fp
->sfp();
1821 if (LIKELY(prevFp
!= nullptr)) {
1823 if (UNLIKELY(fp
->resumed())) {
1824 assert(fp
->func()->isGenerator());
1825 *prevSp
= (TypedValue
*)prevFp
- prevFp
->func()->numSlotsInFrame();
1827 *prevSp
= (TypedValue
*)(fp
+ 1);
1830 if (prevPc
) *prevPc
= prevFp
->func()->base() + fp
->m_soff
;
1831 if (fromVMEntry
) *fromVMEntry
= false;
1834 // Linear search from end of m_nestedVMs. In practice, we're probably
1835 // looking for something recently pushed.
1836 int i
= m_nestedVMs
.size() - 1;
1837 ActRec
* firstAR
= vmFirstAR();
1838 while (i
>= 0 && firstAR
!= fp
) {
1839 firstAR
= m_nestedVMs
[i
--].firstAR
;
1841 if (i
== -1) return nullptr;
1842 const VMState
& vmstate
= m_nestedVMs
[i
];
1843 prevFp
= vmstate
.fp
;
1845 assert(prevFp
->func()->unit());
1846 if (prevSp
) *prevSp
= vmstate
.sp
;
1848 *prevPc
= prevFp
->func()->unit()->offsetOf(vmstate
.pc
);
1850 if (fromVMEntry
) *fromVMEntry
= true;
1855 Instantiate hoistable classes and functions.
1856 If there is any more work left to do, setup a
1857 new frame ready to execute the pseudomain.
1859 return true iff the pseudomain needs to be executed.
1861 bool ExecutionContext::evalUnit(Unit
* unit
, PC
& pc
, int funcType
) {
1864 if (unit
->isMergeOnly()) {
1865 Stats::inc(Stats::PseudoMain_Skipped
);
1866 *vmStack().allocTV() = *unit
->getMainReturn();
1869 Stats::inc(Stats::PseudoMain_Executed
);
1871 ActRec
* ar
= vmStack().allocA();
1872 auto const cls
= vmfp()->func()->cls();
1873 auto const func
= unit
->getMain(cls
);
1874 assert(!func
->isCPPBuiltin());
1877 ar
->setThisOrClass(vmfp()->getThisOrClass());
1878 if (ar
->hasThis()) ar
->getThis()->incRefCount();
1884 ar
->setReturn(vmfp(), pc
, jit::tc::ustubs().retHelper
);
1885 pushFrameSlots(func
);
1887 auto prevFp
= vmfp();
1888 if (UNLIKELY(prevFp
->skipFrame())) {
1889 prevFp
= g_context
->getPrevVMStateSkipFrame(prevFp
);
1892 assertx(prevFp
->func()->attrs() & AttrMayUseVV
);
1893 if (!prevFp
->hasVarEnv()) {
1894 prevFp
->setVarEnv(VarEnv::createLocal(prevFp
));
1896 ar
->m_varEnv
= prevFp
->m_varEnv
;
1897 ar
->m_varEnv
->enterFP(prevFp
, ar
);
1900 pc
= func
->getEntry();
1902 bool ret
= EventHook::FunctionCall(vmfp(), funcType
);
1904 checkStack(vmStack(), func
, 0);
1908 Variant
ExecutionContext::getEvaledArg(const StringData
* val
,
1909 const String
& namespacedName
,
1910 const Unit
* funcUnit
) {
1911 auto key
= StrNR(val
);
1913 if (m_evaledArgs
.get()) {
1914 auto const arg
= m_evaledArgs
.get()->get(key
);
1915 if (!arg
.is_dummy()) return Variant::wrap(arg
.tv());
1919 int pos
= namespacedName
.rfind('\\');
1921 auto ns
= namespacedName
.substr(0, pos
);
1922 code
= (funcUnit
->isHHFile() ? s_hh_namespace
: s_php_namespace
) +
1923 ns
+ s_curly_return
+ key
+ s_semicolon_curly
;
1925 code
= (funcUnit
->isHHFile() ? s_hh_return
: s_php_return
) +
1928 Unit
* unit
= compileEvalString(code
.get());
1929 assert(unit
!= nullptr);
1930 // Default arg values are not currently allowed to depend on class context.
1931 auto v
= Variant::attach(
1932 g_context
->invokeFunc(unit
->getMain(nullptr),
1933 init_null_variant
, nullptr, nullptr, nullptr, nullptr,
1936 Variant
&lv
= m_evaledArgs
.lvalAt(key
, AccessFlags::Key
);
1941 void ExecutionContext::recordLastError(const Exception
&e
, int errnum
) {
1942 m_lastError
= String(e
.getMessage());
1943 m_lastErrorNum
= errnum
;
1944 m_lastErrorPath
= String::attach(getContainingFileName());
1945 m_lastErrorLine
= getLine();
1946 if (auto const ee
= dynamic_cast<const ExtendedException
*>(&e
)) {
1947 m_lastErrorPath
= ee
->getFileAndLine().first
;
1948 m_lastErrorLine
= ee
->getFileAndLine().second
;
1952 void ExecutionContext::clearLastError() {
1953 m_lastError
= String();
1955 m_lastErrorPath
= staticEmptyString();
1956 m_lastErrorLine
= 0;
1959 void ExecutionContext::enqueueAPCHandle(APCHandle
* handle
, size_t size
) {
1960 assert(handle
->isUncounted());
1961 if (RuntimeOption::EvalGCForAPC
) {
1962 // Register handle with APCGCManager
1963 // And resursively find all allocations belong to handle, register them too
1964 APCGCManager::getInstance().registerPendingDeletion(handle
, size
);
1966 m_apcHandles
.push_back(handle
);
1967 m_apcMemSize
+= size
;
1970 // Treadmill solution for the SharedVariant memory management
1972 struct FreedAPCHandle
{
1973 explicit FreedAPCHandle(std::vector
<APCHandle
*>&& shandles
, size_t size
)
1974 : m_memSize(size
), m_apcHandles(std::move(shandles
))
1977 if (RuntimeOption::EvalGCForAPC
) {
1978 // Treadmill ask APCGCManager to free the handles
1979 APCGCManager::getInstance().freeAPCHandles(m_apcHandles
);
1981 for (auto handle
: m_apcHandles
) {
1982 APCTypedValue::fromHandle(handle
)->deleteUncounted();
1985 APCStats::getAPCStats().removePendingDelete(m_memSize
);
1989 std::vector
<APCHandle
*> m_apcHandles
;
1993 void ExecutionContext::manageAPCHandle() {
1994 assert(apcExtension::UseUncounted
|| m_apcHandles
.size() == 0);
1995 if (m_apcHandles
.size() > 0) {
1996 std::vector
<APCHandle
*> handles
;
1997 handles
.swap(m_apcHandles
);
1999 FreedAPCHandle(std::move(handles
), m_apcMemSize
)
2001 APCStats::getAPCStats().addPendingDelete(m_apcMemSize
);
2005 void ExecutionContext::destructObjects() {
2006 if (UNLIKELY(RuntimeOption::EnableObjDestructCall
)) {
2007 while (!m_liveBCObjs
.empty()) {
2008 ObjectData
* obj
= *m_liveBCObjs
.begin();
2009 obj
->destructForExit(); // Let the instance remove the node.
2011 m_liveBCObjs
.clear();
2015 // Evaled units have a footprint in the TC and translation metadata. The
2016 // applications we care about tend to have few, short, stereotyped evals,
2017 // where the same code keeps getting eval'ed over and over again; so we
2018 // keep around units for each eval'ed string, so that the TC space isn't
2019 // wasted on each eval.
2020 typedef RankedCHM
<StringData
*, HPHP::Unit
*,
2021 StringDataHashCompare
,
2022 RankEvaledUnits
> EvaledUnitsMap
;
2023 static EvaledUnitsMap s_evaledUnits
;
2024 Unit
* ExecutionContext::compileEvalString(
2026 const char* evalFilename
/* = nullptr */) {
2027 EvaledUnitsMap::accessor acc
;
2028 // Promote this to a static string; otherwise it may get swept
2030 code
= makeStaticString(code
);
2031 if (s_evaledUnits
.insert(acc
, code
)) {
2032 acc
->second
= compile_string(
2041 StrNR
ExecutionContext::createFunction(const String
& args
,
2042 const String
& code
) {
2043 if (UNLIKELY(RuntimeOption::EvalAuthoritativeMode
)) {
2044 // Whole program optimizations need to assume they can see all the
2046 raise_error("You can't use create_function in RepoAuthoritative mode; "
2047 "use a closure instead");
2051 auto const ar
= GetCallerFrame();
2052 // It doesn't matter if there's a user function named __lambda_func; we only
2053 // use this name during parsing, and then change it to an impossible name
2054 // with a NUL byte before we merge it into the request's func map. This also
2055 // has the bonus feature that the value of __FUNCTION__ inside the created
2056 // function will match Zend. (Note: Zend will actually fatal if there's a
2057 // user function named __lambda_func when you call create_function. Huzzah!)
2058 static StringData
* oldName
= makeStaticString("__lambda_func");
2059 std::ostringstream codeStr
;
2060 codeStr
<< (ar
->unit()->isHHFile() ? "<?hh" : "<?php")
2061 << " function " << oldName
->data()
2062 << "(" << args
.data() << ") {"
2063 << code
.data() << "}\n";
2064 std::string evalCode
= codeStr
.str();
2065 Unit
* unit
= compile_string(evalCode
.data(), evalCode
.size());
2066 // Move the function to a different name.
2067 std::ostringstream newNameStr
;
2068 newNameStr
<< '\0' << "lambda_" << ++m_lambdaCounter
;
2069 StringData
* newName
= makeStaticString(newNameStr
.str());
2070 unit
->renameFunc(oldName
, newName
);
2071 m_createdFuncs
.push_back(unit
);
2074 // At the end of the request we clear the m_createdFunc map, JIT'ing the unit
2075 // would be a waste of time and TC space.
2076 unit
->setInterpretOnly();
2078 // Technically we shouldn't have to eval the unit right now (it'll execute
2079 // the pseudo-main, which should be empty) and could get away with just
2080 // mergeFuncs. However, Zend does it this way, as proven by the fact that you
2081 // can inject code into the evaled unit's pseudo-main:
2083 // create_function('', '} echo "hi"; if (0) {');
2085 // We have to eval now to emulate this behavior.
2087 invokeFunc(unit
->getMain(nullptr), init_null_variant
,
2088 nullptr, nullptr, nullptr, nullptr,
2092 // __lambda_func will be the only hoistable function.
2093 // Any functions or closures defined in it will not be hoistable.
2094 Func
* lambda
= unit
->firstHoistable();
2095 return lambda
->nameStr();
2098 ExecutionContext::EvaluationResult
2099 ExecutionContext::evalPHPDebugger(StringData
* code
, int frame
) {
2100 // The code has "<?php" prepended already
2101 auto unit
= compile_string(code
->data(), code
->size());
2102 if (unit
== nullptr) {
2103 raise_error("Syntax error");
2104 return {true, init_null_variant
, "Syntax error"};
2107 return evalPHPDebugger(unit
, frame
);
2110 ExecutionContext::EvaluationResult
2111 ExecutionContext::evalPHPDebugger(Unit
* unit
, int frame
) {
2112 always_assert(!RuntimeOption::RepoAuthoritative
);
2114 // Do not JIT this unit, we are using it exactly once.
2115 unit
->setInterpretOnly();
2121 if (fp
->skipFrame()) fp
= getPrevVMStateSkipFrame(fp
);
2122 for (; frame
> 0; --frame
) {
2123 auto prevFp
= getPrevVMStateSkipFrame(fp
);
2125 // To be safe in case we failed to get prevFp. This would mean we've
2126 // been asked to eval in a frame which is beyond the top of the stack.
2127 // This suggests the debugger client has made an error.
2132 if (!fp
->hasVarEnv()) {
2133 fp
->setVarEnv(VarEnv::createLocal(fp
));
2136 ObjectData
*this_
= nullptr;
2137 // NB: the ActRec and function within the AR may have different classes. The
2138 // class in the ActRec is the type used when invoking the function (i.e.,
2139 // Derived in Derived::Foo()) while the class obtained from the function is
2140 // the type that declared the function Foo, which may be Base. We need both
2141 // the class to match any object that this function may have been invoked on,
2142 // and we need the class from the function execution is stopped in.
2143 Class
*frameClass
= nullptr;
2144 Class
*functionClass
= nullptr;
2146 functionClass
= fp
->m_func
->cls();
2147 if (functionClass
) {
2148 if (fp
->hasThis()) {
2149 this_
= fp
->getThis();
2150 } else if (fp
->hasClass()) {
2151 frameClass
= fp
->getClass();
2154 phpDebuggerEvalHook(fp
->m_func
);
2157 const static StaticString
s_cppException("Hit an exception");
2158 const static StaticString
s_phpException("Hit a php exception");
2159 const static StaticString
s_exit("Hit exit");
2160 const static StaticString
s_fatal("Hit fatal");
2161 std::ostringstream errorString
;
2164 // Find a suitable PC to use when switching to the target frame. If the target
2165 // is the current frame, this is just vmpc(). For other cases, this will
2166 // generally be the return address from a call from that frame's function. If
2167 // we can't find the target frame (because it lies deeper in the stack), then
2168 // just use the target frame's func's entry point.
2169 auto const findSuitablePC
= [this](const ActRec
* target
){
2170 if (auto fp
= vmfp()) {
2171 if (fp
== target
) return vmpc();
2173 auto prevFp
= getPrevVMState(fp
);
2175 if (prevFp
== target
) return prevFp
->func()->getEntry() + fp
->m_soff
;
2179 return target
->func()->getEntry();
2183 // Start with the correct parent FP so that VarEnv can properly exitFP().
2184 // Note that if the same VarEnv is used across multiple frames, the most
2185 // recent FP must be used. This can happen if we are trying to debug
2186 // an eval() call or a call issued by debugger itself.
2188 // We also need to change vmpc() to match, since we assert in a few places
2189 // that the vmpc() lies within vmfp()'s code.
2190 auto savedFP
= vmfp();
2191 auto savedPC
= vmpc();
2193 auto newFp
= fp
->m_varEnv
->getFP();
2194 assertx(!newFp
->skipFrame());
2195 vmpc() = findSuitablePC(newFp
);
2198 SCOPE_EXIT
{ vmpc() = savedPC
; vmfp() = savedFP
; };
2200 // Invoke the given PHP, possibly specialized to match the type of the
2201 // current function on the stack, optionally passing a this pointer or
2202 // class used to execute the current function.
2203 return {false, Variant::attach(
2204 invokeFunc(unit
->getMain(functionClass
), init_null_variant
,
2205 this_
, frameClass
, fp
? fp
->m_varEnv
: nullptr, nullptr,
2208 } catch (FatalErrorException
&e
) {
2209 errorString
<< s_fatal
.data();
2210 errorString
<< " : ";
2211 errorString
<< e
.getMessage().c_str();
2212 errorString
<< "\n";
2213 stack
= ExtendedLogger::StringOfStackTrace(e
.getBacktrace());
2214 } catch (ExitException
&e
) {
2215 errorString
<< s_exit
.data();
2216 errorString
<< " : ";
2217 errorString
<< ExitException::ExitCode
;
2218 } catch (Eval::DebuggerException
&e
) {
2219 } catch (Exception
&e
) {
2220 errorString
<< s_cppException
.data();
2221 errorString
<< " : ";
2222 errorString
<< e
.getMessage().c_str();
2223 ExtendedException
* ee
= dynamic_cast<ExtendedException
*>(&e
);
2225 errorString
<< "\n";
2226 stack
= ExtendedLogger::StringOfStackTrace(ee
->getBacktrace());
2228 } catch (Object
&e
) {
2229 errorString
<< s_phpException
.data();
2230 errorString
<< " : ";
2232 errorString
<< e
->invokeToString().data();
2234 errorString
<< e
->getVMClass()->name()->data();
2237 errorString
<< s_cppException
.data();
2240 auto errorStr
= errorString
.str();
2241 g_context
->write(errorStr
);
2242 if (!stack
.empty()) {
2243 g_context
->write(stack
.c_str());
2246 return {true, init_null_variant
, errorStr
};
2249 void ExecutionContext::enterDebuggerDummyEnv() {
2250 static Unit
* s_debuggerDummy
= compile_string("<?php?>", 7);
2251 // Ensure that the VM stack is completely empty (vmfp() should be null)
2252 // and that we're not in a nested VM (reentrancy)
2253 assert(vmfp() == nullptr);
2254 assert(m_nestedVMs
.size() == 0);
2255 assert(m_nesting
== 0);
2256 assert(vmStack().count() == 0);
2257 ActRec
* ar
= vmStack().allocA();
2258 ar
->m_func
= s_debuggerDummy
->getMain(nullptr);
2261 ar
->setReturnVMExit();
2263 vmpc() = s_debuggerDummy
->entry();
2265 vmfp()->setVarEnv(m_globalVarEnv
);
2266 m_globalVarEnv
->enterFP(nullptr, vmfp());
2269 void ExecutionContext::exitDebuggerDummyEnv() {
2270 assert(m_globalVarEnv
);
2271 // Ensure that vmfp() is valid
2272 assert(vmfp() != nullptr);
2273 // Ensure that vmfp() points to the only frame on the call stack.
2274 // In other words, make sure there are no VM frames directly below
2275 // this one and that we are not in a nested VM (reentrancy)
2276 assert(!vmfp()->sfp());
2277 assert(m_nestedVMs
.size() == 0);
2278 assert(m_nesting
== 0);
2279 // Teardown the frame we erected by enterDebuggerDummyEnv()
2280 const Func
* func
= vmfp()->m_func
;
2282 vmfp()->setLocalsDecRefd();
2283 frame_free_locals_no_hook(vmfp());
2285 vmStack().ndiscard(func
->numSlotsInFrame());
2286 vmStack().discardAR();
2287 // After tearing down this frame, the VM stack should be completely empty
2288 assert(vmStack().count() == 0);
2293 ThrowAllErrorsSetter::ThrowAllErrorsSetter() {
2294 m_throwAllErrors
= g_context
->getThrowAllErrors();
2295 g_context
->setThrowAllErrors(true);
2298 ThrowAllErrorsSetter::~ThrowAllErrorsSetter() {
2299 g_context
->setThrowAllErrors(m_throwAllErrors
);