Kill debugger's usage of pseudomains and varenv
[hiphop-php.git] / hphp / runtime / base / execution-context.cpp
blobbfd4c0d1320e84595c579b6c0643a231adad01f4
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/execution-context.h"
18 #define __STDC_LIMIT_MACROS
20 #include <cstdint>
21 #include <algorithm>
22 #include <list>
23 #include <utility>
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/memory-manager.h"
38 #include "hphp/runtime/base/sweepable.h"
39 #include "hphp/runtime/base/backtrace.h"
40 #include "hphp/runtime/base/builtin-functions.h"
41 #include "hphp/runtime/base/comparisons.h"
42 #include "hphp/runtime/base/program-functions.h"
43 #include "hphp/runtime/base/runtime-option.h"
44 #include "hphp/runtime/base/tv-refcount.h"
45 #include "hphp/runtime/base/tv-type.h"
46 #include "hphp/runtime/base/type-variant.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/implicit-context.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/jit/unwind-itanium.h"
68 #include "hphp/runtime/vm/act-rec-defs.h"
69 #include "hphp/runtime/vm/debugger-hook.h"
70 #include "hphp/runtime/vm/event-hook.h"
71 #include "hphp/runtime/vm/hh-utils.h"
72 #include "hphp/runtime/vm/interp-helpers.h"
73 #include "hphp/runtime/vm/resumable.h"
74 #include "hphp/runtime/vm/runtime.h"
75 #include "hphp/runtime/vm/runtime-compiler.h"
76 #include "hphp/runtime/vm/treadmill.h"
77 #include "hphp/runtime/vm/unwind.h"
78 #include "hphp/runtime/base/php-globals.h"
79 #include "hphp/runtime/base/exceptions.h"
80 #include "hphp/zend/zend-math.h"
82 namespace HPHP {
83 ///////////////////////////////////////////////////////////////////////////////
84 TRACE_SET_MOD(bcinterp);
86 rds::local::AliasedRDSLocal<ExecutionContext,
87 rds::local::Initialize::Explicitly,
88 &rds::local::detail::HotRDSLocals::g_context
89 > g_context;
91 ExecutionContext::ExecutionContext()
92 : m_transport(nullptr)
93 , m_sb(nullptr)
94 , m_implicitFlush(false)
95 , m_protectedLevel(0)
96 , m_stdoutBytesWritten(0)
97 , m_errorState(ExecutionContext::ErrorState::NoError)
98 , m_lastErrorNum(0)
99 , m_deferredErrors(nullptr)
100 , m_throwAllErrors(false)
101 , m_pageletTasksStarted(0)
102 , m_vhost(nullptr)
103 , m_globalVarEnv(nullptr)
104 , m_lambdaCounter(0)
105 , m_nesting(0)
106 , m_dbgNoBreak(false)
107 , m_lastErrorPath(staticEmptyString())
108 , m_lastErrorLine(0)
109 , m_executingSetprofileCallback(false)
110 , m_logger_hook(*this)
112 ARRPROV_USE_RUNTIME_LOCATION();
113 m_deferredErrors = staticEmptyVec();
114 resetCoverageCounters();
115 // We don't want a new execution context to cause any request-heap
116 // allocations (because it will cause us to hold a slab, even while idle).
117 static auto s_cwd = makeStaticString(Process::CurrentWorkingDirectory);
118 m_cwd = s_cwd;
119 RID().setMemoryLimit(std::to_string(RuntimeOption::RequestMemoryMaxBytes));
120 RID().setErrorReportingLevel(RuntimeOption::RuntimeErrorReportingLevel);
122 VariableSerializer::serializationSizeLimit->value =
123 RuntimeOption::SerializationSizeLimit;
124 tvWriteUninit(m_headerCallback);
126 m_shutdowns[ShutdownType::ShutDown] = empty_vec_array();
127 m_shutdowns[ShutdownType::PostSend] = empty_vec_array();
128 m_shutdownsBackup[ShutdownType::ShutDown] = empty_vec_array();
129 m_shutdownsBackup[ShutdownType::PostSend] = empty_vec_array();
132 namespace rds { namespace local {
133 // See header for why this is required.
134 #ifndef _MSC_VER
135 template<>
136 #endif
137 void rds::local::RDSLocal<ExecutionContext,
138 rds::local::Initialize::Explicitly>::destroy() {
139 if (!isNull()) {
140 getNoCheck()->sweep();
141 nullOut();
147 void ExecutionContext::cleanup() {
148 manageAPCHandle();
149 auto dead = std::move(m_stdoutHooks);
152 void ExecutionContext::sweep() {
153 cleanup();
156 ExecutionContext::~ExecutionContext() {
157 cleanup();
160 void ExecutionContext::backupSession() {
161 m_shutdownsBackup = m_shutdowns;
162 m_userErrorHandlersBackup = m_userErrorHandlers;
163 m_userExceptionHandlersBackup = m_userExceptionHandlers;
166 void ExecutionContext::restoreSession() {
167 m_shutdowns = m_shutdownsBackup;
168 m_userErrorHandlers = m_userErrorHandlersBackup;
169 m_userExceptionHandlers = m_userExceptionHandlersBackup;
172 ///////////////////////////////////////////////////////////////////////////////
173 // system functions
175 String ExecutionContext::getMimeType() const {
176 String mimetype;
177 if (m_transport) {
178 mimetype = m_transport->getMimeType();
181 if (strncasecmp(mimetype.data(), "text/", 5) == 0) {
182 int pos = mimetype.find(';');
183 if (pos != String::npos) {
184 mimetype = mimetype.substr(0, pos);
186 } else if (m_transport && m_transport->getUseDefaultContentType()) {
187 mimetype = RID().getDefaultMimeType();
189 return mimetype;
192 std::string ExecutionContext::getRequestUrl(size_t szLimit) {
193 Transport* t = getTransport();
194 std::string ret = t ? t->getUrl() : "";
195 if (szLimit != std::string::npos) {
196 ret = ret.substr(0, szLimit);
198 return ret;
201 void ExecutionContext::setContentType(const String& mimetype,
202 const String& charset) {
203 if (m_transport) {
204 String contentType = mimetype;
205 contentType += "; ";
206 contentType += "charset=";
207 contentType += charset;
208 m_transport->addHeader("Content-Type", contentType.c_str());
209 m_transport->setUseDefaultContentType(false);
213 ///////////////////////////////////////////////////////////////////////////////
214 // write()
216 void ExecutionContext::write(const String& s) {
217 write(s.data(), s.size());
220 void ExecutionContext::addStdoutHook(StdoutHook* hook) {
221 if (hook != nullptr) {
222 m_stdoutHooks.insert(hook);
226 bool ExecutionContext::removeStdoutHook(StdoutHook* hook) {
227 if (hook == nullptr) {
228 return false;
231 return m_stdoutHooks.erase(hook) != 0;
234 static void safe_stdout(const void *ptr, size_t size) {
235 write(fileno(stdout), ptr, size);
238 void ExecutionContext::writeStdout(const char *s, int len) {
239 fflush(stdout);
240 if (m_stdoutHooks.empty()) {
241 if (s_stdout_color) {
242 safe_stdout(s_stdout_color, strlen(s_stdout_color));
243 safe_stdout(s, len);
244 safe_stdout(ANSI_COLOR_END, strlen(ANSI_COLOR_END));
245 } else {
246 safe_stdout(s, len);
248 m_stdoutBytesWritten += len;
249 } else {
250 for (auto const hook : m_stdoutHooks) {
251 assertx(hook != nullptr);
252 (*hook)(s, len);
257 void ExecutionContext::writeTransport(const char *s, int len) {
258 if (m_transport) {
259 m_transport->sendRaw(s, len, 200, false, true);
260 } else {
261 writeStdout(s, len);
265 size_t ExecutionContext::getStdoutBytesWritten() const {
266 return m_stdoutBytesWritten;
269 void ExecutionContext::write(const char *s, int len) {
270 if (m_sb) {
271 m_sb->append(s, len);
272 if (m_out && m_out->chunk_size > 0) {
273 if (m_sb->size() >= m_out->chunk_size) {
274 obFlush();
277 if (m_implicitFlush) flush();
278 } else {
279 writeTransport(s, len);
283 ///////////////////////////////////////////////////////////////////////////////
284 // output buffers
286 void ExecutionContext::obProtect(bool on) {
287 m_protectedLevel = on ? m_buffers.size() : 0;
290 void ExecutionContext::obStart(const Variant& handler /* = null */,
291 int chunk_size /* = 0 */,
292 OBFlags flags /* = OBFlags::Default */) {
293 if (m_insideOBHandler) {
294 raise_error("ob_start(): Cannot use output buffering "
295 "in output buffering display handlers");
297 m_buffers.emplace_back(Variant(handler), chunk_size, flags);
298 resetCurrentBuffer();
301 String ExecutionContext::obCopyContents() {
302 if (!m_buffers.empty()) {
303 StringBuffer &oss = m_buffers.back().oss;
304 if (!oss.empty()) {
305 return oss.copy();
308 return empty_string();
311 String ExecutionContext::obDetachContents() {
312 if (!m_buffers.empty()) {
313 StringBuffer &oss = m_buffers.back().oss;
314 if (!oss.empty()) {
315 return oss.detach();
318 return empty_string();
321 int ExecutionContext::obGetContentLength() {
322 if (m_buffers.empty()) {
323 return 0;
325 return m_buffers.back().oss.size();
328 void ExecutionContext::obClean(int handler_flag) {
329 if (!m_buffers.empty()) {
330 OutputBuffer *last = &m_buffers.back();
331 if (!last->handler.isNull()) {
332 m_insideOBHandler = true;
333 SCOPE_EXIT { m_insideOBHandler = false; };
334 vm_call_user_func(last->handler,
335 make_vec_array(last->oss.detach(), handler_flag));
337 last->oss.clear();
341 bool ExecutionContext::obFlush(bool force /*= false*/) {
342 assertx(m_protectedLevel >= 0);
344 if ((int)m_buffers.size() <= m_protectedLevel) {
345 return false;
348 auto iter = m_buffers.end();
349 OutputBuffer& last = *(--iter);
350 if (!force && !(last.flags & OBFlags::Flushable)) {
351 return false;
353 if (any(last.flags & OBFlags::OutputDisabled)) {
354 return false;
357 const int flag = k_PHP_OUTPUT_HANDLER_START | k_PHP_OUTPUT_HANDLER_END;
359 if (iter != m_buffers.begin()) {
360 OutputBuffer& prev = *(--iter);
361 if (last.handler.isNull()) {
362 prev.oss.absorb(last.oss);
363 } else {
364 auto str = last.oss.detach();
365 try {
366 Variant tout;
368 ARRPROV_USE_RUNTIME_LOCATION();
369 m_insideOBHandler = true;
370 SCOPE_EXIT { m_insideOBHandler = false; };
371 tout = vm_call_user_func(
372 last.handler, make_vec_array(str, flag)
375 prev.oss.append(tout.toString());
376 } catch (...) {
377 prev.oss.append(str);
378 throw;
381 return true;
384 auto str = last.oss.detach();
385 if (!last.handler.isNull()) {
386 try {
387 Variant tout;
389 ARRPROV_USE_RUNTIME_LOCATION();
390 m_insideOBHandler = true;
391 SCOPE_EXIT { m_insideOBHandler = false; };
392 tout = vm_call_user_func(
393 last.handler, make_vec_array(str, flag)
396 str = tout.toString();
397 } catch (...) {
398 writeTransport(str.data(), str.size());
399 throw;
403 writeTransport(str.data(), str.size());
404 return true;
407 void ExecutionContext::obFlushAll() {
408 do {
409 obFlush(true);
410 } while (obEnd());
413 bool ExecutionContext::obEnd() {
414 assertx(m_protectedLevel >= 0);
415 if ((int)m_buffers.size() > m_protectedLevel) {
416 m_buffers.pop_back();
417 resetCurrentBuffer();
418 if (m_implicitFlush) flush();
419 return true;
421 if (m_implicitFlush) flush();
422 return false;
425 void ExecutionContext::obEndAll() {
426 while (obEnd()) {}
429 int ExecutionContext::obGetLevel() {
430 assertx((int)m_buffers.size() >= m_protectedLevel);
431 return m_buffers.size() - m_protectedLevel;
434 const StaticString
435 s_level("level"),
436 s_type("type"),
437 s_flags("flags"),
438 s_name("name"),
439 s_args("args"),
440 s_chunk_size("chunk_size"),
441 s_buffer_used("buffer_used"),
442 s_default_output_handler("default output handler");
444 Array ExecutionContext::obGetStatus(bool full) {
445 Array ret = empty_varray();
446 int level = 0;
447 for (auto& buffer : m_buffers) {
448 Array status = empty_darray();
449 if (level < m_protectedLevel || buffer.handler.isNull()) {
450 status.set(s_name, s_default_output_handler);
451 status.set(s_type, 0);
452 } else {
453 status.set(s_name, buffer.handler);
454 status.set(s_type, 1);
457 int flags = 0;
458 if (any(buffer.flags & OBFlags::Cleanable)) {
459 flags |= k_PHP_OUTPUT_HANDLER_CLEANABLE;
461 if (any(buffer.flags & OBFlags::Flushable)) {
462 flags |= k_PHP_OUTPUT_HANDLER_FLUSHABLE;
464 if (any(buffer.flags & OBFlags::Removable)) {
465 flags |= k_PHP_OUTPUT_HANDLER_REMOVABLE;
467 status.set(s_flags, flags);
469 status.set(s_level, level);
470 status.set(s_chunk_size, buffer.chunk_size);
471 status.set(s_buffer_used, static_cast<uint64_t>(buffer.oss.size()));
473 if (full) {
474 ret.append(status);
475 } else {
476 ret = std::move(status);
478 level++;
480 return ret;
483 String ExecutionContext::obGetBufferName() {
484 if (m_buffers.empty()) {
485 return String();
486 } else if (m_buffers.size() <= m_protectedLevel) {
487 return s_default_output_handler;
488 } else {
489 auto iter = m_buffers.end();
490 OutputBuffer& buffer = *(--iter);
491 if (buffer.handler.isNull()) {
492 return s_default_output_handler;
493 } else {
494 return buffer.handler.toString();
499 void ExecutionContext::obSetImplicitFlush(bool on) {
500 m_implicitFlush = on;
503 Array ExecutionContext::obGetHandlers() {
504 Array ret = empty_varray();
505 for (auto& ob : m_buffers) {
506 auto& handler = ob.handler;
507 ret.append(handler.isNull() ? s_default_output_handler : handler);
509 return ret;
512 void ExecutionContext::flush() {
513 if (!m_buffers.empty() &&
514 RuntimeOption::EnableEarlyFlush && m_protectedLevel &&
515 !(m_buffers.front().flags & OBFlags::OutputDisabled)) {
516 OutputBuffer &buffer = m_buffers.front();
517 StringBuffer &oss = buffer.oss;
518 if (!oss.empty()) {
519 if (any(buffer.flags & OBFlags::WriteToStdout)) {
520 writeStdout(oss.data(), oss.size());
521 } else {
522 writeTransport(oss.data(), oss.size());
524 oss.clear();
529 void ExecutionContext::resetCurrentBuffer() {
530 if (m_buffers.empty()) {
531 m_sb = nullptr;
532 m_out = nullptr;
533 } else {
534 m_sb = &m_buffers.back().oss;
535 m_out = &m_buffers.back();
539 ///////////////////////////////////////////////////////////////////////////////
540 // program executions
542 void ExecutionContext::registerShutdownFunction(const Variant& function,
543 ShutdownType type) {
544 auto& funcs = m_shutdowns[type];
545 assertx(funcs.isVec());
546 funcs.append(function);
549 Variant ExecutionContext::pushUserErrorHandler(const Variant& function,
550 int error_types) {
551 Variant ret;
552 if (!m_userErrorHandlers.empty()) {
553 ret = m_userErrorHandlers.back().first;
555 m_userErrorHandlers.push_back(std::pair<Variant,int>(function, error_types));
556 return ret;
559 Variant ExecutionContext::pushUserExceptionHandler(const Variant& function) {
560 Variant ret;
561 if (!m_userExceptionHandlers.empty()) {
562 ret = m_userExceptionHandlers.back();
564 m_userExceptionHandlers.push_back(function);
565 return ret;
568 void ExecutionContext::popUserErrorHandler() {
569 if (!m_userErrorHandlers.empty()) {
570 m_userErrorHandlers.pop_back();
574 void ExecutionContext::clearUserErrorHandlers() {
575 while (!m_userErrorHandlers.empty()) m_userErrorHandlers.pop_back();
578 void ExecutionContext::popUserExceptionHandler() {
579 if (!m_userExceptionHandlers.empty()) {
580 m_userExceptionHandlers.pop_back();
584 void ExecutionContext::acceptRequestEventHandlers(bool enable) {
585 m_acceptRequestEventHandlers = enable;
588 std::size_t ExecutionContext::registerRequestEventHandler(
589 RequestEventHandler *handler) {
590 assertx(handler && handler->getInited());
591 assertx(m_acceptRequestEventHandlers);
592 m_requestEventHandlers.push_back(handler);
593 return m_requestEventHandlers.size()-1;
596 void ExecutionContext::unregisterRequestEventHandler(
597 RequestEventHandler* handler,
598 std::size_t index) {
599 assertx(index < m_requestEventHandlers.size() &&
600 m_requestEventHandlers[index] == handler);
601 assertx(!handler->getInited());
602 if (index == m_requestEventHandlers.size()-1) {
603 m_requestEventHandlers.pop_back();
604 } else {
605 m_requestEventHandlers[index] = nullptr;
609 static bool requestEventHandlerPriorityComp(RequestEventHandler *a,
610 RequestEventHandler *b) {
611 if (!a) return b;
612 else if (!b) return false;
613 else return a->priority() < b->priority();
616 void ExecutionContext::onRequestShutdown() {
617 while (!m_requestEventHandlers.empty()) {
618 // handlers could cause other handlers to be registered,
619 // so need to repeat until done
620 decltype(m_requestEventHandlers) tmp;
621 tmp.swap(m_requestEventHandlers);
623 // Sort handlers by priority so that lower priority values get shutdown
624 // first
625 sort(tmp.begin(), tmp.end(),
626 requestEventHandlerPriorityComp);
627 for (auto* handler : tmp) {
628 if (!handler) continue;
629 assertx(handler->getInited());
630 handler->requestShutdown();
631 handler->setInited(false);
636 void ExecutionContext::executeFunctions(ShutdownType type) {
637 ARRPROV_USE_RUNTIME_LOCATION();
638 RID().resetTimers(
639 RuntimeOption::PspTimeoutSeconds,
640 RuntimeOption::PspCpuTimeoutSeconds
643 if (RO::EvalEnableImplicitContext) {
644 // If the main request terminated with a C++ exception, we would not
645 // have cleared the implicit context since that logic is
646 // done in a PHP try-finally. Let's clear the implicit context here.
647 *ImplicitContext::activeCtx = nullptr;
650 // We mustn't destroy any callbacks until we're done with all
651 // of them. So hold them in tmp.
652 // XXX still true in a world without destructors?
653 auto tmp = empty_vec_array();
654 while (true) {
655 Array funcs = m_shutdowns[type];
656 if (funcs.empty()) break;
657 m_shutdowns[type] = empty_vec_array();
658 IterateV(
659 funcs.get(),
660 [](TypedValue v) {
661 vm_call_user_func(VarNR{v}, init_null_variant);
662 // Implicit context should not have leaked between each call
663 assertx(!RO::EvalEnableImplicitContext ||
664 !(*ImplicitContext::activeCtx));
667 tmp.append(funcs);
671 void ExecutionContext::onShutdownPreSend() {
672 // in case obStart was called without obFlush
673 SCOPE_EXIT {
674 try { obFlushAll(); } catch (...) {}
677 // When host is OOMing, abort abruptly.
678 if (RID().shouldOOMAbort()) return;
680 tracing::Block _{"shutdown-pre-send"};
682 tl_heap->resetCouldOOM(isStandardRequest());
683 executeFunctions(ShutDown);
686 void ExecutionContext::debuggerExecutePsps() {
687 try {
688 executeFunctions(PostSend);
689 } catch (const ExitException& e) {
690 // do nothing
691 } catch (const Exception& e) {
692 onFatalError(e);
693 } catch (const Object& e) {
694 onUnhandledException(e);
695 } catch (...) {
699 void ExecutionContext::onShutdownPostSend() {
700 // When host is OOMing, abort abruptly.
701 if (RID().shouldOOMAbort()) return;
703 tracing::Block _{"shutdown-post-send"};
705 ServerStats::SetThreadMode(ServerStats::ThreadMode::PostProcessing);
706 tl_heap->resetCouldOOM(isStandardRequest());
707 try {
708 try {
709 ServerStatsHelper ssh("psp", ServerStatsHelper::TRACK_HWINST);
710 executeFunctions(PostSend);
711 } catch (...) {
712 try {
713 bump_counter_and_rethrow(true /* isPsp */);
714 } catch (const ExitException& e) {
715 // do nothing
716 } catch (const Exception& e) {
717 onFatalError(e);
718 } catch (const Object& e) {
719 onUnhandledException(e);
722 } catch (...) {
723 Logger::Error("unknown exception was thrown from psp");
726 ServerStats::SetThreadMode(ServerStats::ThreadMode::Idling);
729 ///////////////////////////////////////////////////////////////////////////////
730 // error handling
732 bool ExecutionContext::errorNeedsHandling(int errnum,
733 bool callUserHandler,
734 ErrorThrowMode mode) {
735 if (UNLIKELY(m_throwAllErrors)) {
736 throw Exception(folly::sformat("throwAllErrors: {}", errnum));
738 if (mode != ErrorThrowMode::Never || errorNeedsLogging(errnum)) {
739 return true;
741 if (callUserHandler) {
742 if (!m_userErrorHandlers.empty() &&
743 (m_userErrorHandlers.back().second & errnum) != 0) {
744 return true;
747 return false;
750 bool ExecutionContext::errorNeedsLogging(int errnum) {
751 auto level =
752 RID().getErrorReportingLevel() |
753 RuntimeOption::ForceErrorReportingLevel;
754 return RuntimeOption::NoSilencer || (level & errnum) != 0;
757 struct ErrorStateHelper {
758 ErrorStateHelper(ExecutionContext *context,
759 ExecutionContext::ErrorState state) {
760 m_context = context;
761 m_originalState = m_context->getErrorState();
762 m_context->setErrorState(state);
764 ~ErrorStateHelper() {
765 m_context->setErrorState(m_originalState);
767 private:
768 ExecutionContext *m_context;
769 ExecutionContext::ErrorState m_originalState;
772 const StaticString
773 s_class("class"),
774 s_file("file"),
775 s_function("function"),
776 s_line("line"),
777 s_error_num("error-num"),
778 s_error_string("error-string"),
779 s_error_file("error-file"),
780 s_error_line("error-line"),
781 s_error_backtrace("error-backtrace"),
782 s_overflow("overflow");
784 void ExecutionContext::handleError(const std::string& msg,
785 int errnum,
786 bool callUserHandler,
787 ErrorThrowMode mode,
788 const std::string& prefix,
789 bool skipFrame /* = false */) {
790 SYNC_VM_REGS_SCOPED();
792 auto newErrorState = ErrorState::ErrorRaised;
793 switch (getErrorState()) {
794 case ErrorState::ErrorRaised:
795 case ErrorState::ErrorRaisedByUserHandler:
796 return;
797 case ErrorState::ExecutingUserHandler:
798 newErrorState = ErrorState::ErrorRaisedByUserHandler;
799 break;
800 default:
801 break;
804 // Potentially upgrade the error to E_USER_ERROR
805 if (errnum & RuntimeOption::ErrorUpgradeLevel &
806 static_cast<int>(ErrorMode::UPGRADEABLE_ERROR)) {
807 errnum = static_cast<int>(ErrorMode::USER_ERROR);
808 mode = ErrorThrowMode::IfUnhandled;
811 auto const ee = skipFrame ?
812 ExtendedException(ExtendedException::SkipFrame{}, msg) :
813 ExtendedException(msg);
814 bool handled = false;
816 ErrorStateHelper esh(this, newErrorState);
817 if (callUserHandler) {
818 handled = callUserErrorHandler(ee, errnum, false);
821 if (!handled) {
822 recordLastError(ee, errnum);
825 if (g_system_profiler) {
826 g_system_profiler->errorCallBack(ee, errnum, msg);
830 if (mode == ErrorThrowMode::Always ||
831 (mode == ErrorThrowMode::IfUnhandled && !handled)) {
832 DEBUGGER_ATTACHED_ONLY(phpDebuggerErrorHook(ee, errnum, msg));
833 bool isRecoverable =
834 errnum == static_cast<int>(ErrorMode::RECOVERABLE_ERROR);
835 raise_fatal_error(msg.c_str(), ee.getBacktrace(), isRecoverable,
836 !errorNeedsLogging(errnum) /* silent */);
837 not_reached();
839 if (!handled) {
843 // If we're inside an error handler already, queue it up on the deferred
844 // list.
845 if (getErrorState() == ErrorState::ExecutingUserHandler) {
846 auto& deferred = m_deferredErrors;
847 if (deferred.size() < RuntimeOption::EvalMaxDeferredErrors) {
848 auto fileAndLine = ee.getFileAndLine();
849 deferred.append(
850 make_dict_array(
851 s_error_num, errnum,
852 s_error_string, msg,
853 s_error_file, std::move(fileAndLine.first),
854 s_error_line, fileAndLine.second,
855 s_error_backtrace, ee.getBacktrace()
858 } else if (!deferred.empty()) {
859 auto const last = deferred.lval(int64_t{deferred.size() - 1});
860 if (isDictType(type(last))) {
861 asArrRef(last).set(s_overflow, true);
866 if (errorNeedsLogging(errnum)) {
867 DEBUGGER_ATTACHED_ONLY(phpDebuggerErrorHook(ee, errnum, ee.getMessage()));
868 auto fileAndLine = ee.getFileAndLine();
869 Logger::Log(Logger::LogError, prefix.c_str(), ee,
870 fileAndLine.first.c_str(), fileAndLine.second);
875 bool ExecutionContext::callUserErrorHandler(const Exception& e, int errnum,
876 bool swallowExceptions) {
877 switch (getErrorState()) {
878 case ErrorState::ExecutingUserHandler:
879 case ErrorState::ErrorRaisedByUserHandler:
880 return false;
881 default:
882 break;
884 if (!m_userErrorHandlers.empty() &&
885 (m_userErrorHandlers.back().second & errnum) != 0) {
886 auto fileAndLine = std::make_pair(empty_string(), 0);
887 Variant backtrace;
888 if (auto const ee = dynamic_cast<const ExtendedException*>(&e)) {
889 fileAndLine = ee->getFileAndLine();
890 backtrace = ee->getBacktrace();
892 try {
893 ARRPROV_USE_RUNTIME_LOCATION();
894 ErrorStateHelper esh(this, ErrorState::ExecutingUserHandler);
895 m_deferredErrors = empty_vec_array();
896 SCOPE_EXIT { m_deferredErrors = empty_vec_array(); };
897 if (!same(vm_call_user_func
898 (m_userErrorHandlers.back().first,
899 make_vec_array(errnum, String(e.getMessage()),
900 fileAndLine.first, fileAndLine.second, empty_darray(),
901 backtrace)),
902 false)) {
903 return true;
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;
930 } catch (...) {
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;
941 return false;
944 bool ExecutionContext::onFatalError(const Exception& e) {
945 tl_heap->resetCouldOOM(isStandardRequest());
946 RID().resetTimers();
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(),
971 fileAndLine.second);
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(),
979 fileAndLine.second);
981 return handled;
984 bool ExecutionContext::onUnhandledException(Object e) {
985 String err = throwable_to_string(e.get());
986 if (RuntimeOption::AlwaysLogUnhandledExceptions) {
987 Logger::Error("\nFatal error: Uncaught %s", err.data());
990 if (e.instanceof(SystemLib::s_ThrowableClass)) {
991 ARRPROV_USE_RUNTIME_LOCATION();
992 // user thrown exception
993 if (!m_userExceptionHandlers.empty()) {
994 if (!same(vm_call_user_func
995 (m_userExceptionHandlers.back(),
996 make_vec_array(e)),
997 false)) {
998 return true;
1001 } else {
1002 assertx(false);
1004 m_lastError = err;
1006 if (!RuntimeOption::AlwaysLogUnhandledExceptions) {
1007 Logger::Error("\nFatal error: Uncaught %s", err.data());
1009 return false;
1012 ///////////////////////////////////////////////////////////////////////////////
1014 void ExecutionContext::debuggerInfo(
1015 std::vector<std::pair<const char*,std::string>>& info) {
1016 int64_t newInt = convert_bytes_to_long(IniSetting::Get("memory_limit"));
1017 if (newInt <= 0) {
1018 newInt = std::numeric_limits<int64_t>::max();
1020 if (newInt == std::numeric_limits<int64_t>::max()) {
1021 info.emplace_back("Max Memory", "(unlimited)");
1022 } else {
1023 info.emplace_back("Max Memory", IDebuggable::FormatSize(newInt));
1025 info.emplace_back("Max Time",
1026 IDebuggable::FormatTime(RID().getTimeout() * 1000));
1029 void ExecutionContext::setenv(const String& name, const String& value) {
1030 m_envs.set(m_envs.convertKey<IntishCast::Cast>(name),
1031 make_tv<KindOfString>(value.get()));
1034 void ExecutionContext::unsetenv(const String& name) {
1035 m_envs.remove(name);
1038 String ExecutionContext::getenv(const String& name) const {
1039 if (m_envs.exists(name)) {
1040 return m_envs[name].toString();
1042 if (is_cli_server_mode()) {
1043 auto envs = cli_env();
1044 if (envs.exists(name)) return envs[name].toString();
1045 return String();
1047 if (auto value = ::getenv(name.data())) {
1048 return String(value, CopyString);
1050 if (RuntimeOption::EnvVariables.find(name.c_str()) != RuntimeOption::EnvVariables.end()) {
1051 return String(RuntimeOption::EnvVariables[name.c_str()].data(), CopyString);
1053 return String();
1056 TypedValue ExecutionContext::lookupClsCns(const NamedEntity* ne,
1057 const StringData* cls,
1058 const StringData* cns) {
1059 Class* class_ = nullptr;
1060 try {
1061 class_ = Unit::loadClass(ne, cls);
1062 } catch (Object& ex) {
1063 // For compatibility with php, throwing through a constant lookup has
1064 // different behavior inside a property initializer (86pinit/86sinit).
1065 auto ar = getStackFrame();
1066 if (ar && ar->func() && Func::isSpecial(ar->func()->name())) {
1067 raise_warning("Uncaught %s", throwable_to_string(ex.get()).data());
1068 raise_error("Couldn't find constant %s::%s", cls->data(), cns->data());
1070 throw;
1072 if (class_ == nullptr) {
1073 raise_error(Strings::UNKNOWN_CLASS, cls->data());
1075 TypedValue clsCns = class_->clsCnsGet(cns);
1076 if (clsCns.m_type == KindOfUninit) {
1077 raise_error("Couldn't find constant %s::%s", cls->data(), cns->data());
1079 return clsCns;
1082 static Class* loadClass(StringData* clsName) {
1083 Class* class_ = Unit::loadClass(clsName);
1084 if (class_ == nullptr) {
1085 raise_error(Strings::UNKNOWN_CLASS, clsName->data());
1087 return class_;
1090 ObjectData* ExecutionContext::createObject(StringData* clsName,
1091 const Variant& params,
1092 bool init /* = true */) {
1093 return createObject(loadClass(clsName), params, init);
1096 ObjectData* ExecutionContext::createObject(const Class* class_,
1097 const Variant& params,
1098 bool init) {
1099 callerDynamicConstructChecks(class_);
1100 auto o = Object::attach(ObjectData::newInstance(const_cast<Class*>(class_)));
1101 if (init) {
1102 initObject(class_, params, o.get());
1105 return o.detach();
1108 ObjectData* ExecutionContext::createObjectOnly(StringData* clsName) {
1109 return createObject(clsName, init_null_variant, false);
1112 ObjectData* ExecutionContext::initObject(StringData* clsName,
1113 const Variant& params,
1114 ObjectData* o) {
1115 return initObject(loadClass(clsName), params, o);
1118 ObjectData* ExecutionContext::initObject(const Class* class_,
1119 const Variant& params,
1120 ObjectData* o) {
1121 auto ctor = class_->getCtor();
1122 if (!(ctor->attrs() & AttrPublic)) {
1123 std::string msg = "Access to non-public constructor of class ";
1124 msg += class_->name()->data();
1125 Reflection::ThrowReflectionExceptionObject(msg);
1127 // call constructor
1128 if (!isContainerOrNull(params)) {
1129 throw_param_is_not_container();
1131 tvDecRefGen(invokeFunc(ctor, params, o, nullptr, true, false, true));
1132 return o;
1135 ActRec* ExecutionContext::getStackFrame() {
1136 VMRegAnchor _;
1137 return vmfp();
1140 ObjectData* ExecutionContext::getThis() {
1141 VMRegAnchor _;
1142 ActRec* fp = vmfp();
1143 if (fp->skipFrame()) fp = getPrevVMStateSkipFrame(fp);
1144 if (fp && fp->func()->cls() && fp->hasThis()) {
1145 return fp->getThis();
1147 return nullptr;
1150 const RepoOptions& ExecutionContext::getRepoOptionsForCurrentFrame() const {
1151 VMRegAnchor _;
1153 if (auto const ar = vmfp()) {
1154 auto const path = ar->func()->unit()->filepath();
1155 return RepoOptions::forFile(path->data());
1157 return RepoOptions::defaults();
1160 void ExecutionContext::onLoadWithOptions(
1161 const char* f, const RepoOptions& opts
1163 if (!RuntimeOption::EvalFatalOnParserOptionMismatch) return;
1164 if (!m_requestOptions) {
1165 m_requestOptions.emplace(opts);
1166 return;
1168 if (m_requestOptions != opts) {
1169 // The data buffer has to stay alive for the call to raise_error.
1170 auto const path_str = opts.path();
1171 auto const path = path_str.empty() ? "{default options}" : path_str.data();
1172 raise_error(
1173 "Attempting to load file %s with incompatible parser settings from %s, "
1174 "this request is using parser settings from %s",
1175 f, path, m_requestOptions->path().data()
1180 StringData* ExecutionContext::getContainingFileName() {
1181 VMRegAnchor _;
1182 ActRec* ar = vmfp();
1183 if (ar == nullptr) return staticEmptyString();
1184 if (ar->skipFrame()) ar = getPrevVMStateSkipFrame(ar);
1185 if (ar == nullptr) return staticEmptyString();
1186 Unit* unit = ar->m_func->unit();
1187 auto const path = ar->m_func->originalFilename() ?
1188 ar->m_func->originalFilename() : unit->filepath();
1189 return const_cast<StringData*>(path);
1192 int ExecutionContext::getLine() {
1193 VMRegAnchor _;
1194 ActRec* ar = vmfp();
1195 Unit* unit = ar ? ar->m_func->unit() : nullptr;
1196 Offset pc = unit ? pcOff() : 0;
1197 if (ar == nullptr) return -1;
1198 if (ar->skipFrame()) ar = getPrevVMStateSkipFrame(ar, &pc);
1199 if (ar == nullptr || (unit = ar->m_func->unit()) == nullptr) return -1;
1200 return unit->getLineNumber(pc);
1203 ActRec* ExecutionContext::getFrameAtDepthForDebuggerUnsafe(int frameDepth) {
1204 ActRec* ret = nullptr;
1205 walkStack([&] (ActRec* fp, Offset) {
1206 if (frameDepth == 0) {
1207 if (fp && !fp->localsDecRefd()) {
1208 ret = fp;
1210 return true;
1213 frameDepth--;
1214 return false;
1216 return ret;
1219 void ExecutionContext::setVar(StringData* name, tv_rval v) {
1220 VMRegAnchor _;
1221 ActRec *fp = vmfp();
1222 if (!fp) return;
1223 if (fp->skipFrame()) fp = getPrevVMStateSkipFrame(fp);
1224 if (fp) fp->getVarEnv()->set(name, v);
1227 Array ExecutionContext::getLocalDefinedVariablesDebugger(int frame) {
1228 const auto fp = getFrameAtDepthForDebuggerUnsafe(frame);
1229 return getDefinedVariables(fp);
1232 bool ExecutionContext::setHeaderCallback(const Variant& callback) {
1233 if (tvAsVariant(g_context->m_headerCallback).toBoolean()) {
1234 // return false if a callback has already been set.
1235 return false;
1237 tvAsVariant(g_context->m_headerCallback) = callback;
1238 return true;
1241 const static StaticString
1242 s_enter_async_entry_point("__SystemLib\\enter_async_entry_point");
1244 TypedValue ExecutionContext::invokeUnit(const Unit* unit,
1245 bool callByHPHPInvoke) {
1246 checkHHConfig(unit);
1248 if (!unit->isHHFile()) {
1249 throw PhpNotSupportedException(unit->filepath()->data());
1252 const_cast<Unit*>(unit)->merge();
1254 auto it = unit->getCachedEntryPoint();
1255 if (callByHPHPInvoke && it != nullptr) {
1256 if (it->isAsync()) {
1257 invokeFunc(
1258 Unit::lookupFunc(s_enter_async_entry_point.get()),
1259 make_vec_array_tagged(ARRPROV_HERE(), Variant{it}),
1260 nullptr, nullptr, false
1262 } else {
1263 invokeFunc(it, init_null_variant, nullptr, nullptr, false);
1266 return make_tv<KindOfInt64>(1);
1269 void ExecutionContext::syncGdbState() {
1270 if (RuntimeOption::EvalJit && !RuntimeOption::EvalJitNoGdb) {
1271 Debug::DebugInfo::Get()->debugSync();
1275 void ExecutionContext::pushVMState(TypedValue* savedSP) {
1276 if (UNLIKELY(!vmfp())) {
1277 // first entry
1278 assertx(m_nestedVMs.size() == 0);
1279 return;
1282 TRACE(3, "savedVM: %p %p %p %p\n", vmpc(), vmfp(), vmFirstAR(), savedSP);
1284 auto& savedVM = m_nestedVMs.emplace_back(
1285 VMState {
1286 vmpc(),
1287 vmfp(),
1288 vmFirstAR(),
1289 savedSP,
1290 vmMInstrState(),
1291 vmJitCalledFrame(),
1292 vmJitReturnAddr(),
1293 jit::g_unwind_rds->exn,
1294 jit::g_unwind_rds->sideEnter
1297 jit::g_unwind_rds->exn = nullptr;
1298 jit::g_unwind_rds->sideEnter = false;
1300 m_nesting++;
1302 if (debug && savedVM.fp &&
1303 savedVM.fp->m_func &&
1304 savedVM.fp->m_func->unit()) {
1305 // Some asserts and tracing.
1306 const Func* func = savedVM.fp->m_func;
1307 /* bound-check asserts in offsetOf */
1308 func->unit()->offsetOf(savedVM.pc);
1309 TRACE(3, "pushVMState: saving frame %s pc %p off %d fp %p\n",
1310 func->name()->data(),
1311 savedVM.pc,
1312 func->unit()->offsetOf(savedVM.pc),
1313 savedVM.fp);
1317 void ExecutionContext::popVMState() {
1318 if (UNLIKELY(m_nestedVMs.empty())) {
1319 // last exit
1320 vmfp() = nullptr;
1321 vmpc() = nullptr;
1322 vmFirstAR() = nullptr;
1323 return;
1326 assertx(m_nestedVMs.size() >= 1);
1328 VMState &savedVM = m_nestedVMs.back();
1329 vmpc() = savedVM.pc;
1330 vmfp() = savedVM.fp;
1331 vmFirstAR() = savedVM.firstAR;
1332 vmStack().top() = savedVM.sp;
1333 vmMInstrState() = savedVM.mInstrState;
1334 vmJitCalledFrame() = savedVM.jitCalledFrame;
1335 vmJitReturnAddr() = savedVM.jitReturnAddr;
1336 jit::g_unwind_rds->exn = savedVM.exn;
1337 jit::g_unwind_rds->sideEnter = savedVM.unwinderSideEnter;
1339 if (debug) {
1340 if (savedVM.fp &&
1341 savedVM.fp->m_func &&
1342 savedVM.fp->m_func->unit()) {
1343 const Func* func = savedVM.fp->m_func;
1344 (void) /* bound-check asserts in offsetOf */
1345 func->unit()->offsetOf(savedVM.pc);
1346 TRACE(3, "popVMState: restoring frame %s pc %p off %d fp %p\n",
1347 func->name()->data(),
1348 savedVM.pc,
1349 func->unit()->offsetOf(savedVM.pc),
1350 savedVM.fp);
1354 m_nestedVMs.pop_back();
1355 m_nesting--;
1357 TRACE(1, "Reentry: exit fp %p pc %p\n", vmfp(), vmpc());
1360 void ExecutionContext::ExcLoggerHook::operator()(
1361 const char* header, const char* msg, const char* ending
1363 ec.write(header);
1364 ec.write(msg);
1365 ec.write(ending);
1366 ec.flush();
1369 StaticString
1370 s_php("<?php "),
1371 s_hh("<?hh "),
1372 s_namespace("namespace "),
1373 s_curly_start(" { "),
1374 s_curly_end(" }"),
1375 s_function_start("<<__DynamicallyCallable>> function "),
1376 s_evaluate_default_argument("evaluate_default_argument"),
1377 s_function_middle("() { return "),
1378 s_semicolon(";"),
1379 s_stdclass("stdclass");
1381 void ExecutionContext::requestInit() {
1382 assertx(SystemLib::s_unit);
1384 initBlackHole();
1385 VarEnv::createGlobal();
1386 vmStack().requestInit();
1387 ResourceHdr::resetMaxId();
1388 jit::tc::requestInit();
1390 if (RuntimeOption::EvalJitEnableRenameFunction) {
1391 assertx(SystemLib::s_anyNonPersistentBuiltins);
1395 * The normal case for production mode is that all builtins are
1396 * persistent, and every systemlib unit is accordingly going to be
1397 * merge only.
1399 * However, if we have rename_function generally enabled, or if any
1400 * builtin functions were specified as interceptable at
1401 * repo-generation time, we'll actually need to merge systemlib on
1402 * every request because some of the builtins will not be marked
1403 * persistent.
1405 if (UNLIKELY(SystemLib::s_anyNonPersistentBuiltins)) {
1406 SystemLib::s_unit->merge();
1407 SystemLib::mergePersistentUnits();
1408 if (SystemLib::s_hhas_unit) SystemLib::s_hhas_unit->merge();
1409 } else {
1410 // System units are merge only, and everything is persistent.
1411 assertx(SystemLib::s_unit->isEmpty());
1412 assertx(!SystemLib::s_hhas_unit || SystemLib::s_hhas_unit->isEmpty());
1415 if (RO::EvalEnableImplicitContext) *ImplicitContext::activeCtx = nullptr;
1417 profileRequestStart();
1419 HHProf::Request::StartProfiling();
1421 #ifndef NDEBUG
1422 Class* cls = NamedEntity::get(s_stdclass.get())->clsList();
1423 assertx(cls);
1424 assertx(cls == SystemLib::s_stdclassClass);
1425 #endif
1427 if (Logger::UseRequestLog) Logger::SetThreadHook(&m_logger_hook);
1429 // Needs to be last (or nearly last): might cause unit merging to call an
1430 // extension function in the VM; this is bad if systemlib itself hasn't been
1431 // merged.
1432 autoTypecheckRequestInit();
1435 void ExecutionContext::requestExit() {
1436 apcExtension::purgeDeferred(std::move(m_apcDeferredExpire));
1438 autoTypecheckRequestExit();
1439 HHProf::Request::FinishProfiling();
1441 manageAPCHandle();
1442 syncGdbState();
1443 vmStack().requestExit();
1444 profileRequestEnd();
1445 EventHook::Disable();
1446 zend_rand_unseed();
1447 clearBlackHole();
1449 if (m_globalVarEnv) {
1450 req::destroy_raw(m_globalVarEnv);
1451 m_globalVarEnv = nullptr;
1454 if (!m_lastError.isNull()) {
1455 clearLastError();
1459 ARRPROV_USE_RUNTIME_LOCATION();
1460 m_deferredErrors = empty_vec_array();
1463 if (Logger::UseRequestLog) Logger::SetThreadHook(nullptr);
1464 if (m_requestTrace) record_trace(std::move(*m_requestTrace));
1467 template<class Action>
1468 static inline void enterVM(ActRec* ar, Action action) {
1469 enterVMCustomHandler(ar, [&] { exception_handler(action); });
1473 * Shared implementation for invokeFunc{,Few}().
1475 * The `doEnterVM' callback take an ActRec* argument corresponding to
1476 * the reentry frame.
1478 template<class FEnterVM>
1479 ALWAYS_INLINE
1480 TypedValue ExecutionContext::invokeFuncImpl(const Func* f,
1481 ObjectData* thiz, Class* cls,
1482 uint32_t numArgsInclUnpack,
1483 FEnterVM doEnterVM) {
1484 assertx(f);
1485 // If `f' is a regular function, `thiz' and `cls' must be null.
1486 assertx(IMPLIES(!f->implCls(), (!thiz && !cls)));
1487 // If `f' is a method, either `thiz' or `cls' must be non-null.
1488 assertx(IMPLIES(f->preClass(), thiz || cls));
1489 // If `f' is a static method, thiz must be null.
1490 assertx(IMPLIES(f->isStaticInPrologue(), !thiz));
1492 if (thiz != nullptr) thiz->incRefCount();
1494 ActRec* ar = vmStack().indA(numArgsInclUnpack);
1495 ar->setReturnVMExit();
1496 ar->m_func = f;
1497 if (thiz) {
1498 ar->setThis(thiz);
1499 } else if (cls) {
1500 ar->setClass(cls);
1501 } else {
1502 ar->trashThis();
1504 ar->setNumArgs(numArgsInclUnpack);
1505 ar->trashVarEnv();
1507 #ifdef HPHP_TRACE
1508 if (vmfp() == nullptr) {
1509 TRACE(1, "Reentry: enter %s(%p) from top-level\n",
1510 f->name()->data(), ar);
1511 } else {
1512 TRACE(1, "Reentry: enter %s(pc %p ar %p) from %s(%p)\n",
1513 f->name()->data(), vmpc(), ar,
1514 vmfp()->m_func ? vmfp()->m_func->name()->data()
1515 : "unknownBuiltin",
1516 vmfp());
1518 #endif
1520 auto const reentrySP =
1521 vmStack().top() + numArgsInclUnpack + kNumActRecCells + f->numInOutParams();
1522 pushVMState(reentrySP);
1523 SCOPE_EXIT {
1524 assert_flog(
1525 vmStack().top() == reentrySP,
1526 "vmsp() mismatch around reentry: before @ {}, after @ {}",
1527 reentrySP, vmStack().top()
1529 popVMState();
1532 enterVM(ar, [&] { doEnterVM(ar); });
1534 if (UNLIKELY(f->takesInOutParams())) {
1535 // This is OK (albeit ugly) since the return value should only be readable
1536 // from user code via e.g. call_user_func and we will tagTV the result from
1537 // the native wrapper and getting the correct frame pointer here (or from
1538 // the normal arrprov::tagFromPC) is awkward.
1539 ARRPROV_USE_RUNTIME_LOCATION();
1541 VArrayInit varr(f->numInOutParams() + 1);
1542 for (uint32_t i = 0; i < f->numInOutParams() + 1; ++i) {
1543 varr.append(*vmStack().topTV());
1544 vmStack().popC();
1546 auto arr = varr.toArray();
1547 return make_array_like_tv(arr.detach());
1548 } else {
1549 auto const retval = *vmStack().topTV();
1550 vmStack().discard();
1551 return retval;
1556 * Enter VM by calling action(), which invokes a function or resumes
1557 * an async function. The 'ar' argument points to an ActRec of the
1558 * invoked/resumed function.
1560 template<class Action>
1561 static inline void enterVMCustomHandler(ActRec* ar, Action action) {
1562 assertx(ar);
1563 assertx(!ar->sfp());
1564 assertx(isReturnHelper(reinterpret_cast<void*>(ar->m_savedRip)));
1565 assertx(ar->callOffset() == 0);
1567 vmFirstAR() = ar;
1568 vmJitCalledFrame() = nullptr;
1569 vmJitReturnAddr() = 0;
1571 action();
1573 while (vmpc()) {
1574 exception_handler(enterVMAtCurPC);
1578 TypedValue ExecutionContext::invokeFunc(const Func* f,
1579 const Variant& args_,
1580 ObjectData* thiz /* = NULL */,
1581 Class* cls /* = NULL */,
1582 bool dynamic /* = true */,
1583 bool checkRefAnnot /* = false */,
1584 bool allowDynCallNoPointer
1585 /* = false */,
1586 Array&& reifiedGenerics
1587 /* = Array() */) {
1588 VMRegAnchor _;
1590 // We must do a stack overflow check for leaf functions on re-entry,
1591 // because we won't have checked that the stack is deep enough for a
1592 // leaf function /after/ re-entry, and the prologue for the leaf
1593 // function will not make a check.
1594 if (f->isPhpLeafFn() ||
1595 !(f->numParams() <= kStackCheckReenterPadding - kNumActRecCells)) {
1596 // Check both the native stack and VM stack for overflow, numParams
1597 // is already included in f->maxStackCells().
1598 checkStack(vmStack(), f, f->numInOutParams() + kNumActRecCells);
1599 } else {
1600 // invokeFunc() must always check the native stack for overflow no
1601 // matter what.
1602 checkNativeStack();
1605 // Reserve space for inout outputs and ActRec.
1606 for (auto i = f->numInOutParams(); i > 0; --i) vmStack().pushUninit();
1607 for (auto i = kNumActRecCells; i > 0; --i) vmStack().pushUninit();
1609 // Push arguments.
1610 auto const& args = *args_.asTypedValue();
1611 assertx(isContainerOrNull(args));
1612 auto numArgs = tvIsNull(args) ? 0 : getContainerSize(args);
1613 if (numArgs > 0) {
1614 assertx(isContainer(args));
1615 tvDup(args, *vmStack().allocC());
1616 numArgs = prepareUnpackArgs(f, 0, checkRefAnnot);
1619 // Caller checks.
1620 if (dynamic) callerDynamicCallChecks(f, allowDynCallNoPointer);
1622 auto const doEnterVM = [&] (ActRec* ar) {
1623 enterVMAtFunc(ar, std::move(reifiedGenerics), f->takesInOutParams(),
1624 dynamic, allowDynCallNoPointer);
1627 return invokeFuncImpl(f, thiz, cls, numArgs, doEnterVM);
1630 TypedValue ExecutionContext::invokeFuncFew(
1631 const Func* f,
1632 ExecutionContext::ThisOrClass thisOrCls,
1633 uint32_t numArgs,
1634 const TypedValue* argv,
1635 bool dynamic /* = true */,
1636 bool allowDynCallNoPointer
1637 /* = false */
1639 VMRegAnchor _;
1640 auto& stack = vmStack();
1642 // See comments in invokeFunc().
1643 if (f->isPhpLeafFn() ||
1644 !(numArgs <= kStackCheckReenterPadding - kNumActRecCells)) {
1645 checkStack(stack, f, f->numInOutParams() + kNumActRecCells);
1646 } else {
1647 checkNativeStack();
1650 // Reserve space for inout outputs and ActRec.
1651 for (auto i = f->numInOutParams(); i > 0; --i) stack.pushUninit();
1652 for (auto i = kNumActRecCells; i > 0; --i) stack.pushUninit();
1654 // Push non-variadic arguments.
1655 auto const numParams = f->numNonVariadicParams();
1656 for (auto i = 0; i < numArgs && i < numParams; ++i) {
1657 const TypedValue *from = &argv[i];
1658 TypedValue* to = stack.allocTV();
1659 tvDup(*from, *to);
1662 // Push variadic arguments.
1663 if (UNLIKELY(numParams < numArgs)) {
1664 VArrayInit ai{numArgs - numParams};
1665 for (auto i = numParams; i < numArgs; ++i) ai.append(argv[i]);
1666 if (RuntimeOption::EvalHackArrDVArrs) {
1667 stack.pushVecNoRc(ai.create());
1668 } else {
1669 stack.pushArrayNoRc(ai.create());
1671 numArgs = numParams + 1;
1674 // Caller checks.
1675 if (dynamic) callerDynamicCallChecks(f, allowDynCallNoPointer);
1677 auto const doEnterVM = [&] (ActRec* ar) {
1678 enterVMAtFunc(ar, Array(), f->takesInOutParams(), dynamic, false);
1681 return invokeFuncImpl(f,
1682 thisOrCls.left(),
1683 thisOrCls.right(),
1684 numArgs, doEnterVM);
1687 static void prepareAsyncFuncEntry(ActRec* enterFnAr,
1688 Resumable* resumable,
1689 bool willUnwind) {
1690 assertx(enterFnAr);
1691 assertx(enterFnAr->func()->isAsync());
1692 assertx(isResumed(enterFnAr));
1693 assertx(resumable);
1695 vmfp() = enterFnAr;
1696 // If we're going to unwind, we need to resume at the offset we
1697 // suspended at, so we look up the correct catch trace (not the one
1698 // for the next op).
1699 vmpc() = enterFnAr->func()->unit()->at(
1700 willUnwind
1701 ? resumable->suspendOffset()
1702 : resumable->resumeFromAwaitOffset()
1704 EventHook::FunctionResumeAwait(enterFnAr);
1707 void ExecutionContext::resumeAsyncFunc(Resumable* resumable,
1708 ObjectData* freeObj,
1709 const TypedValue awaitResult) {
1710 assertx(tl_regState == VMRegState::CLEAN);
1711 SCOPE_EXIT { assertx(tl_regState == VMRegState::CLEAN); };
1713 auto fp = resumable->actRec();
1715 if (RO::EvalEnableImplicitContext) {
1716 *ImplicitContext::activeCtx = [&] {
1717 if (!fp->func()->isGenerator()) return frame_afwh(fp)->m_implicitContext;
1718 auto gen = frame_async_generator(fp);
1719 return gen->getWaitHandle()->m_implicitContext;
1720 }();
1723 // We don't need to check for space for the ActRec (unlike generally
1724 // in normal re-entry), because the ActRec isn't on the stack.
1725 checkStack(vmStack(), fp->func(), 0);
1727 TypedValue* savedSP = vmStack().top();
1728 tvDup(awaitResult, *vmStack().allocC());
1730 // decref after awaitResult is on the stack
1731 decRefObj(freeObj);
1733 pushVMState(savedSP);
1734 SCOPE_EXIT { popVMState(); };
1736 enterVM(fp, [&] {
1737 prepareAsyncFuncEntry(fp, resumable, false);
1739 const bool useJit = RID().getJit();
1740 if (LIKELY(useJit && resumable->resumeAddr())) {
1741 Stats::inc(Stats::VMEnter);
1742 jit::enterTC(resumable->resumeAddr());
1743 } else {
1744 enterVMAtCurPC();
1749 void ExecutionContext::resumeAsyncFuncThrow(Resumable* resumable,
1750 ObjectData* freeObj,
1751 ObjectData* exception) {
1752 assertx(exception);
1753 assertx(exception->instanceof(SystemLib::s_ThrowableClass));
1754 assertx(tl_regState == VMRegState::CLEAN);
1755 SCOPE_EXIT { assertx(tl_regState == VMRegState::CLEAN); };
1757 auto fp = resumable->actRec();
1759 if (RO::EvalEnableImplicitContext) {
1760 *ImplicitContext::activeCtx = [&] {
1761 if (!fp->func()->isGenerator()) return frame_afwh(fp)->m_implicitContext;
1762 auto gen = frame_async_generator(fp);
1763 return gen->getWaitHandle()->m_implicitContext;
1764 }();
1767 checkStack(vmStack(), fp->func(), 0);
1769 // decref after we hold reference to the exception
1770 Object e(exception);
1771 decRefObj(freeObj);
1773 pushVMState(vmStack().top());
1774 SCOPE_EXIT { popVMState(); };
1776 enterVMCustomHandler(fp, [&] {
1777 prepareAsyncFuncEntry(fp, resumable, true);
1778 unwindVM(exception);
1779 e.reset();
1783 ActRec* ExecutionContext::getPrevVMState(const ActRec* fp,
1784 Offset* prevPc /* = NULL */,
1785 TypedValue** prevSp /* = NULL */,
1786 bool* fromVMEntry /* = NULL */,
1787 uint64_t* jitReturnAddr /* = NULL */) {
1788 if (fp == nullptr) {
1789 return nullptr;
1791 ActRec* prevFp = fp->sfp();
1792 if (LIKELY(prevFp != nullptr)) {
1793 if (prevSp) {
1794 if (UNLIKELY(isResumed(fp))) {
1795 assertx(fp->func()->isGenerator());
1796 *prevSp = (TypedValue*)prevFp - prevFp->func()->numSlotsInFrame();
1797 } else {
1798 *prevSp = (TypedValue*)(fp + 1);
1801 if (prevPc) *prevPc = prevFp->func()->base() + fp->callOffset();
1802 if (fromVMEntry) *fromVMEntry = false;
1803 return prevFp;
1805 // Linear search from end of m_nestedVMs. In practice, we're probably
1806 // looking for something recently pushed.
1807 int i = m_nestedVMs.size() - 1;
1808 ActRec* firstAR = vmFirstAR();
1809 while (i >= 0 && firstAR != fp) {
1810 firstAR = m_nestedVMs[i--].firstAR;
1812 if (i == -1) return nullptr;
1813 const VMState& vmstate = m_nestedVMs[i];
1814 prevFp = vmstate.fp;
1815 assertx(prevFp);
1816 assertx(prevFp->func()->unit());
1817 if (prevSp) *prevSp = vmstate.sp;
1818 if (prevPc) {
1819 *prevPc = prevFp->func()->unit()->offsetOf(vmstate.pc);
1821 if (fromVMEntry) *fromVMEntry = true;
1822 if (jitReturnAddr) *jitReturnAddr = (uint64_t)vmstate.jitReturnAddr;
1823 return prevFp;
1826 Variant ExecutionContext::getEvaledArg(const StringData* val,
1827 const String& namespacedName,
1828 const Unit* funcUnit) {
1829 auto key = StrNR(val);
1831 if (m_evaledArgs.get()) {
1832 auto const arg = m_evaledArgs.get()->get(key);
1833 if (arg.is_init()) return Variant::wrap(arg);
1836 String code;
1837 String funcName;
1838 int pos = namespacedName.rfind('\\');
1839 if (pos != -1) {
1840 auto ns = namespacedName.substr(0, pos);
1841 code = (funcUnit->isHHFile() ? s_hh : s_php) +
1842 s_namespace + ns + s_curly_start + s_function_start +
1843 s_evaluate_default_argument + s_function_middle + key +
1844 s_semicolon + s_curly_end + s_curly_end;
1845 funcName = ns + "\\" + s_evaluate_default_argument;
1846 } else {
1847 code = (funcUnit->isHHFile() ? s_hh : s_php) +
1848 s_function_start + s_evaluate_default_argument + s_function_middle +
1849 key + s_semicolon + s_curly_end;
1850 funcName = s_evaluate_default_argument;
1853 Unit* unit = compileEvalString(code.get());
1854 assertx(unit != nullptr);
1855 unit->setInterpretOnly();
1857 // The evaluate_default_argument function should be the last one
1858 auto func = *(unit->funcs().end() - 1);
1859 assertx(func->name()->equal(funcName.get()) &&
1860 "We expecting the evaluate_default_argument func");
1862 // Default arg values are not currently allowed to depend on class context.
1863 auto v = Variant::attach(
1864 g_context->invokeFuncFew(func, nullptr, 0, nullptr, true, true)
1866 auto const lv = m_evaledArgs.lvalForce(key, AccessFlags::Key);
1867 tvSet(*v.asTypedValue(), lv);
1868 return Variant::wrap(lv.tv());
1871 void ExecutionContext::recordLastError(const Exception& e, int errnum) {
1872 m_lastError = String(e.getMessage());
1873 m_lastErrorNum = errnum;
1874 m_lastErrorPath = String::attach(getContainingFileName());
1875 m_lastErrorLine = getLine();
1876 if (auto const ee = dynamic_cast<const ExtendedException*>(&e)) {
1877 m_lastErrorPath = ee->getFileAndLine().first;
1878 m_lastErrorLine = ee->getFileAndLine().second;
1882 void ExecutionContext::clearLastError() {
1883 m_lastError = String();
1884 m_lastErrorNum = 0;
1885 m_lastErrorPath = staticEmptyString();
1886 m_lastErrorLine = 0;
1889 void ExecutionContext::enqueueAPCDeferredExpire(const String& key) {
1890 auto keyStr = key.get();
1891 keyStr->incRefCount();
1892 m_apcDeferredExpire.push_back(keyStr);
1895 void ExecutionContext::enqueueAPCHandle(APCHandle* handle, size_t size) {
1896 assertx(handle->isUncounted());
1897 m_apcHandles.push_back(handle);
1898 m_apcMemSize += size;
1901 // Treadmill solution for the SharedVariant memory management
1902 namespace {
1903 struct FreedAPCHandle {
1904 explicit FreedAPCHandle(std::vector<APCHandle*>&& shandles, size_t size)
1905 : m_memSize(size), m_apcHandles(std::move(shandles))
1907 void operator()() {
1908 for (auto handle : m_apcHandles) {
1909 APCTypedValue::fromHandle(handle)->deleteUncounted();
1911 APCStats::getAPCStats().removePendingDelete(m_memSize);
1913 private:
1914 size_t m_memSize;
1915 std::vector<APCHandle*> m_apcHandles;
1919 void ExecutionContext::manageAPCHandle() {
1920 assertx(apcExtension::UseUncounted || m_apcHandles.size() == 0);
1921 if (m_apcHandles.size() > 0) {
1922 Treadmill::enqueue(
1923 FreedAPCHandle(std::move(m_apcHandles), m_apcMemSize)
1925 APCStats::getAPCStats().addPendingDelete(m_apcMemSize);
1929 // Evaled units have a footprint in the TC and translation metadata. The
1930 // applications we care about tend to have few, short, stereotyped evals,
1931 // where the same code keeps getting eval'ed over and over again; so we
1932 // keep around units for each eval'ed string, so that the TC space isn't
1933 // wasted on each eval.
1934 typedef RankedCHM<StringData*, HPHP::Unit*,
1935 StringDataHashCompare,
1936 RankEvaledUnits> EvaledUnitsMap;
1937 static EvaledUnitsMap s_evaledUnits;
1938 Unit* ExecutionContext::compileEvalString(
1939 StringData* code,
1940 const char* evalFilename /* = nullptr */) {
1941 EvaledUnitsMap::accessor acc;
1942 // Promote this to a static string; otherwise it may get swept
1943 // across requests.
1944 code = makeStaticString(code);
1945 if (s_evaledUnits.insert(acc, code)) {
1946 acc->second = compile_string(
1947 code->data(),
1948 code->size(),
1949 evalFilename,
1950 Native::s_noNativeFuncs,
1951 getRepoOptionsForCurrentFrame()
1954 return acc->second;
1957 ExecutionContext::EvaluationResult
1958 ExecutionContext::evalPHPDebugger(StringData* code, int frame) {
1959 // The code has "<?php" prepended already
1960 auto unit = compile_debugger_string(code->data(), code->size(),
1961 getRepoOptionsForCurrentFrame());
1962 if (unit == nullptr) {
1963 raise_error("Syntax error");
1964 return {true, init_null_variant, "Syntax error"};
1967 return evalPHPDebugger(unit, frame);
1970 const StaticString
1971 s_DebuggerMainAttr("__DebuggerMain"),
1972 s_uninitClsName("__uninitSentinel");
1974 ExecutionContext::EvaluationResult
1975 ExecutionContext::evalPHPDebugger(Unit* unit, int frame) {
1976 always_assert(!RuntimeOption::RepoAuthoritative);
1978 // Do not JIT this unit, we are using it exactly once.
1979 unit->setInterpretOnly();
1981 VMRegAnchor _;
1983 auto fp = getFrameAtDepthForDebuggerUnsafe(frame);
1985 // Continue walking up the stack until we find a frame that can have
1986 // a variable environment context attached to it, or we run out out frames.
1987 while (fp && (fp->skipFrame() || fp->isInlined())) {
1988 fp = getPrevVMStateSkipFrame(fp);
1991 if (fp) phpDebuggerEvalHook(fp->m_func);
1993 const static StaticString s_cppException("Hit an exception");
1994 const static StaticString s_phpException("Hit a php exception");
1995 const static StaticString s_exit("Hit exit");
1996 const static StaticString s_fatal("Hit fatal");
1997 std::ostringstream errorString;
1998 std::string stack;
2000 // Find a suitable PC to use when switching to the target frame. If the target
2001 // is the current frame, this is just vmpc(). For other cases, this will
2002 // generally be the address of a call from that frame's function. If we can't
2003 // find the target frame (because it lies deeper in the stack), then just use
2004 // the target frame's func's entry point.
2005 auto const findSuitablePC = [this](const ActRec* target){
2006 if (auto fp = vmfp()) {
2007 if (fp == target) return vmpc();
2008 while (true) {
2009 auto prevFp = getPrevVMState(fp);
2010 if (!prevFp) break;
2011 if (prevFp == target) {
2012 return prevFp->func()->getEntry() + fp->callOffset();
2014 fp = prevFp;
2017 return target->func()->getEntry();
2020 try {
2021 // We also need to change vmpc() to match, since we assert in a few places
2022 // that the vmpc() lies within vmfp()'s code.
2023 auto savedFP = vmfp();
2024 auto savedPC = vmpc();
2025 if (fp) {
2026 vmpc() = findSuitablePC(fp);
2027 vmfp() = fp;
2029 SCOPE_EXIT { vmpc() = savedPC; vmfp() = savedFP; };
2030 unit->merge();
2032 enum VarAction { StoreFrame, StoreVV, StoreEnv };
2034 auto const uninit_cls = Unit::loadClass(s_uninitClsName.get());
2036 auto globals = Array();
2037 auto const global = fp && fp->m_varEnv && fp->m_varEnv->isGlobalScope();
2038 auto& env = m_debuggerEnv;
2040 auto const ctx = fp ? fp->func()->cls() : nullptr;
2041 auto const f = [&] () -> Func* {
2042 for (auto orig_f : unit->funcs()) {
2043 if (orig_f->userAttributes().count(s_DebuggerMainAttr.get())) {
2044 auto const f = ctx ? orig_f->clone(ctx) : orig_f;
2045 if (ctx) {
2046 f->setNewFuncId();
2047 f->setBaseCls(ctx);
2048 if (fp && fp->hasThis()) {
2049 f->setAttrs(Attr(f->attrs() & ~AttrStatic));
2050 } else if (fp && fp->hasClass()) {
2051 f->setAttrs(Attr(f->attrs() | AttrStatic));
2054 assertx(f->numParams() >= 1 && f->numParams() == f->numInOutParams());
2055 return f;
2058 return nullptr;
2059 }();
2060 always_assert(f);
2062 VArrayInit args{f->numParams()};
2063 std::vector<VarAction> actions{f->numParams(), StoreEnv};
2064 std::vector<Id> frameIds;
2065 frameIds.resize(f->numParams(), 0);
2066 auto const appendUninit = [&] {
2067 args.append(make_tv<KindOfObject>(Object{uninit_cls}.detach()));
2069 for (Id id = 0; id < f->numParams() - 1; id++) {
2070 assertx(id < f->numNamedLocals());
2071 assertx(f->params()[id].isInOut());
2072 if (fp) {
2073 auto const idx = fp->func()->lookupVarId(f->localVarName(id));
2074 if (idx != kInvalidId) {
2075 Variant var{tvAsCVarRef(*frame_local(fp, idx))};
2076 if (var.isInitialized()) args.append(var);
2077 else appendUninit();
2078 actions[id] = StoreFrame;
2079 frameIds[id] = idx;
2080 continue;
2082 if (fp->hasVarEnv()) {
2083 auto const tv = fp->getVarEnv()->lookup(f->localVarName(id));
2084 if (tv) {
2085 if (type(tv) != KindOfUninit) args.append(tvAsCVarRef(*tv));
2086 else appendUninit();
2087 actions[id] = StoreVV;
2088 continue;
2092 auto const val = [&]{
2093 if (!global) return env.lookup(StrNR{f->localVarName(id)});
2094 auto const rval = fp->m_varEnv->lookup(f->localVarName(id));
2095 return rval ? *rval : make_tv<KindOfUninit>();
2096 }();
2097 if (val.is_init()) args.append(val);
2098 else appendUninit();
2100 args.append(make_tv<KindOfNull>()); // $__debugger_exn$output
2102 auto const obj = ctx && fp->hasThis() ? fp->getThis() : nullptr;
2103 auto const cls = ctx && fp->hasClass() ? fp->getClass() : nullptr;
2104 auto const arr_tv = invokeFunc(f, args.toArray(), obj, cls, false);
2105 assertx(isArrayLikeType(type(arr_tv)));
2106 assertx(val(arr_tv).parr->size() == f->numParams() + 1);
2107 Array arr = Array::attach(val(arr_tv).parr);
2108 for (Id id = 0; id < f->numParams() - 1; id++) {
2109 auto const tv = arr.lookup(id + 1);
2110 if (isObjectType(type(tv)) &&
2111 val(tv).pobj->instanceof(uninit_cls)) {
2112 switch (actions[id]) {
2113 case StoreFrame:
2114 variant_ref{frame_local(fp, frameIds[id])}.unset();
2115 break;
2116 case StoreVV:
2117 fp->m_varEnv->unset(f->localVarName(id));
2118 break;
2119 case StoreEnv:
2120 if (global) {
2121 fp->m_varEnv->unset(f->localVarName(id));
2122 } else {
2123 env.remove(StrNR{f->localVarName(id)});
2125 break;
2127 continue;
2129 switch (actions[id]) {
2130 case StoreFrame:
2131 variant_ref{frame_local(fp, frameIds[id])} = arr[id + 1];
2132 break;
2133 case StoreVV:
2134 fp->m_varEnv->set(f->localVarName(id), &tv);
2135 break;
2136 case StoreEnv:
2137 if (global) {
2138 fp->m_varEnv->set(f->localVarName(id), &tv);
2139 } else {
2140 env.set(StrNR{f->localVarName(id)}, tv);
2142 break;
2145 auto const exn = arr[int64_t(f->numParams())];
2146 if (exn.isObject()) {
2147 assertx(exn.toObject().instanceof("Throwable"));
2148 throw_object(exn.toObject());
2150 assertx(exn.isNull());
2152 return {false, arr[0], ""};
2153 } catch (FatalErrorException& e) {
2154 errorString << s_fatal.data();
2155 errorString << " : ";
2156 errorString << e.getMessage().c_str();
2157 errorString << "\n";
2158 stack = ExtendedLogger::StringOfStackTrace(e.getBacktrace());
2159 } catch (ExitException& e) {
2160 errorString << s_exit.data();
2161 errorString << " : ";
2162 errorString << *rl_exit_code;
2163 } catch (Eval::DebuggerException& e) {
2164 } catch (Exception& e) {
2165 errorString << s_cppException.data();
2166 errorString << " : ";
2167 errorString << e.getMessage().c_str();
2168 ExtendedException* ee = dynamic_cast<ExtendedException*>(&e);
2169 if (ee) {
2170 errorString << "\n";
2171 stack = ExtendedLogger::StringOfStackTrace(ee->getBacktrace());
2173 } catch (Object &e) {
2174 errorString << s_phpException.data();
2175 errorString << " : ";
2176 try {
2177 errorString << throwable_to_string(e.get()).data();
2178 } catch (...) {
2179 errorString << e->getVMClass()->name()->data();
2181 } catch (...) {
2182 errorString << s_cppException.data();
2185 auto errorStr = errorString.str();
2186 g_context->write(errorStr);
2187 if (!stack.empty()) {
2188 g_context->write(stack.c_str());
2191 return {true, init_null_variant, errorStr};
2194 const StaticString s_include("include");
2196 void ExecutionContext::enterDebuggerDummyEnv() {
2197 static Unit* s_debuggerDummy = compile_debugger_string(
2198 "<?hh", 4, RepoOptions::defaults()
2200 // Ensure that the VM stack is completely empty (vmfp() should be null)
2201 // and that we're not in a nested VM (reentrancy)
2202 assertx(vmfp() == nullptr);
2203 assertx(m_nestedVMs.size() == 0);
2204 assertx(m_nesting == 0);
2205 assertx(vmStack().count() == 0);
2206 ActRec* ar = vmStack().allocA();
2207 ar->m_func = s_debuggerDummy->lookupFuncId(1);
2208 assertx(ar->m_func && ar->m_func->name()->equal(s_include.get()));
2209 ar->setNumArgs(0);
2210 for (int i = 0; i < ar->m_func->numLocals(); ++i) vmStack().pushInt(1);
2211 ar->trashThis();
2212 ar->setReturnVMExit();
2213 vmfp() = ar;
2214 vmpc() = s_debuggerDummy->entry();
2215 vmFirstAR() = ar;
2216 vmfp()->setVarEnv(m_globalVarEnv);
2217 m_globalVarEnv->enterFP(nullptr, vmfp());
2220 void ExecutionContext::exitDebuggerDummyEnv() {
2221 assertx(m_globalVarEnv);
2222 // Ensure that vmfp() is valid
2223 assertx(vmfp() != nullptr);
2224 // Ensure that vmfp() points to the only frame on the call stack.
2225 // In other words, make sure there are no VM frames directly below
2226 // this one and that we are not in a nested VM (reentrancy)
2227 assertx(!vmfp()->sfp());
2228 assertx(m_nestedVMs.size() == 0);
2229 assertx(m_nesting == 0);
2230 // Teardown the frame we erected by enterDebuggerDummyEnv()
2231 const Func* func = vmfp()->m_func;
2232 try {
2233 vmfp()->setLocalsDecRefd();
2234 frame_free_locals_no_hook(vmfp());
2235 } catch (...) {}
2236 vmStack().ndiscard(func->numSlotsInFrame());
2237 vmStack().discardAR();
2238 // After tearing down this frame, the VM stack should be completely empty
2239 assertx(vmStack().count() == 0);
2240 vmfp() = nullptr;
2241 vmpc() = nullptr;
2244 ThrowAllErrorsSetter::ThrowAllErrorsSetter() {
2245 m_throwAllErrors = g_context->getThrowAllErrors();
2246 g_context->setThrowAllErrors(true);
2249 ThrowAllErrorsSetter::~ThrowAllErrorsSetter() {
2250 g_context->setThrowAllErrors(m_throwAllErrors);