Remove .hh_file from hhas
[hiphop-php.git] / hphp / runtime / base / execution-context.cpp
blob8768230655ad6ce14feaccb67585d8c41defef11
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_globalNVTable(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 Array ExecutionContext::getLocalDefinedVariablesDebugger(int frame) {
1220 const auto fp = getFrameAtDepthForDebuggerUnsafe(frame);
1221 return getDefinedVariables(fp);
1224 bool ExecutionContext::setHeaderCallback(const Variant& callback) {
1225 if (tvAsVariant(g_context->m_headerCallback).toBoolean()) {
1226 // return false if a callback has already been set.
1227 return false;
1229 tvAsVariant(g_context->m_headerCallback) = callback;
1230 return true;
1233 const static StaticString
1234 s_enter_async_entry_point("__SystemLib\\enter_async_entry_point");
1236 TypedValue ExecutionContext::invokeUnit(const Unit* unit,
1237 bool callByHPHPInvoke) {
1238 checkHHConfig(unit);
1240 auto ret = invokePseudoMain(unit->getMain(nullptr, false));
1242 auto it = unit->getCachedEntryPoint();
1243 if (callByHPHPInvoke && it != nullptr) {
1244 if (it->isAsync()) {
1245 invokeFunc(
1246 Unit::lookupFunc(s_enter_async_entry_point.get()),
1247 make_vec_array_tagged(ARRPROV_HERE(), Variant{it}),
1248 nullptr, nullptr, false
1250 } else {
1251 invokeFunc(it, init_null_variant, nullptr, nullptr, false);
1254 return ret;
1257 void ExecutionContext::syncGdbState() {
1258 if (RuntimeOption::EvalJit && !RuntimeOption::EvalJitNoGdb) {
1259 Debug::DebugInfo::Get()->debugSync();
1263 void ExecutionContext::pushVMState(TypedValue* savedSP) {
1264 if (UNLIKELY(!vmfp())) {
1265 // first entry
1266 assertx(m_nestedVMs.size() == 0);
1267 return;
1270 TRACE(3, "savedVM: %p %p %p %p\n", vmpc(), vmfp(), vmFirstAR(), savedSP);
1272 auto& savedVM = m_nestedVMs.emplace_back(
1273 VMState {
1274 vmpc(),
1275 vmfp(),
1276 vmFirstAR(),
1277 savedSP,
1278 vmMInstrState(),
1279 vmJitCalledFrame(),
1280 vmJitReturnAddr(),
1281 jit::g_unwind_rds->exn,
1282 jit::g_unwind_rds->sideEnter
1285 jit::g_unwind_rds->exn = nullptr;
1286 jit::g_unwind_rds->sideEnter = false;
1288 m_nesting++;
1290 if (debug && savedVM.fp &&
1291 savedVM.fp->m_func &&
1292 savedVM.fp->m_func->unit()) {
1293 // Some asserts and tracing.
1294 const Func* func = savedVM.fp->m_func;
1295 /* bound-check asserts in offsetOf */
1296 func->unit()->offsetOf(savedVM.pc);
1297 TRACE(3, "pushVMState: saving frame %s pc %p off %d fp %p\n",
1298 func->name()->data(),
1299 savedVM.pc,
1300 func->unit()->offsetOf(savedVM.pc),
1301 savedVM.fp);
1305 void ExecutionContext::popVMState() {
1306 if (UNLIKELY(m_nestedVMs.empty())) {
1307 // last exit
1308 vmfp() = nullptr;
1309 vmpc() = nullptr;
1310 vmFirstAR() = nullptr;
1311 return;
1314 assertx(m_nestedVMs.size() >= 1);
1316 VMState &savedVM = m_nestedVMs.back();
1317 vmpc() = savedVM.pc;
1318 vmfp() = savedVM.fp;
1319 vmFirstAR() = savedVM.firstAR;
1320 vmStack().top() = savedVM.sp;
1321 vmMInstrState() = savedVM.mInstrState;
1322 vmJitCalledFrame() = savedVM.jitCalledFrame;
1323 vmJitReturnAddr() = savedVM.jitReturnAddr;
1324 jit::g_unwind_rds->exn = savedVM.exn;
1325 jit::g_unwind_rds->sideEnter = savedVM.unwinderSideEnter;
1327 if (debug) {
1328 if (savedVM.fp &&
1329 savedVM.fp->m_func &&
1330 savedVM.fp->m_func->unit()) {
1331 const Func* func = savedVM.fp->m_func;
1332 (void) /* bound-check asserts in offsetOf */
1333 func->unit()->offsetOf(savedVM.pc);
1334 TRACE(3, "popVMState: restoring frame %s pc %p off %d fp %p\n",
1335 func->name()->data(),
1336 savedVM.pc,
1337 func->unit()->offsetOf(savedVM.pc),
1338 savedVM.fp);
1342 m_nestedVMs.pop_back();
1343 m_nesting--;
1345 TRACE(1, "Reentry: exit fp %p pc %p\n", vmfp(), vmpc());
1348 void ExecutionContext::ExcLoggerHook::operator()(
1349 const char* header, const char* msg, const char* ending
1351 ec.write(header);
1352 ec.write(msg);
1353 ec.write(ending);
1354 ec.flush();
1357 StaticString
1358 s_hh("<?hh "),
1359 s_namespace("namespace "),
1360 s_curly_start(" { "),
1361 s_curly_end(" }"),
1362 s_function_start("<<__DynamicallyCallable>> function "),
1363 s_evaluate_default_argument("evaluate_default_argument"),
1364 s_function_middle("() { return "),
1365 s_semicolon(";"),
1366 s_stdclass("stdclass");
1368 void ExecutionContext::requestInit() {
1369 assertx(SystemLib::s_unit);
1371 initBlackHole();
1372 createGlobalNVTable();
1373 vmStack().requestInit();
1374 ResourceHdr::resetMaxId();
1375 jit::tc::requestInit();
1377 if (RuntimeOption::EvalJitEnableRenameFunction) {
1378 assertx(SystemLib::s_anyNonPersistentBuiltins);
1382 * The normal case for production mode is that all builtins are
1383 * persistent, and every systemlib unit is accordingly going to be
1384 * merge only.
1386 * However, if we have rename_function generally enabled, or if any
1387 * builtin functions were specified as interceptable at
1388 * repo-generation time, we'll actually need to merge systemlib on
1389 * every request because some of the builtins will not be marked
1390 * persistent.
1392 if (UNLIKELY(SystemLib::s_anyNonPersistentBuiltins)) {
1393 SystemLib::s_unit->merge();
1394 SystemLib::mergePersistentUnits();
1395 if (SystemLib::s_hhas_unit) SystemLib::s_hhas_unit->merge();
1396 } else {
1397 // System units are merge only, and everything is persistent.
1398 assertx(SystemLib::s_unit->isEmpty());
1399 assertx(!SystemLib::s_hhas_unit || SystemLib::s_hhas_unit->isEmpty());
1402 if (RO::EvalEnableImplicitContext) *ImplicitContext::activeCtx = nullptr;
1404 profileRequestStart();
1406 HHProf::Request::StartProfiling();
1408 #ifndef NDEBUG
1409 Class* cls = NamedEntity::get(s_stdclass.get())->clsList();
1410 assertx(cls);
1411 assertx(cls == SystemLib::s_stdclassClass);
1412 #endif
1414 if (Logger::UseRequestLog) Logger::SetThreadHook(&m_logger_hook);
1416 // Needs to be last (or nearly last): might cause unit merging to call an
1417 // extension function in the VM; this is bad if systemlib itself hasn't been
1418 // merged.
1419 autoTypecheckRequestInit();
1422 void ExecutionContext::requestExit() {
1423 apcExtension::purgeDeferred(std::move(m_apcDeferredExpire));
1425 autoTypecheckRequestExit();
1426 HHProf::Request::FinishProfiling();
1428 manageAPCHandle();
1429 syncGdbState();
1430 vmStack().requestExit();
1431 profileRequestEnd();
1432 EventHook::Disable();
1433 zend_rand_unseed();
1434 clearBlackHole();
1436 if (m_globalNVTable) {
1437 req::destroy_raw(m_globalNVTable);
1438 m_globalNVTable = nullptr;
1441 if (!m_lastError.isNull()) {
1442 clearLastError();
1446 ARRPROV_USE_RUNTIME_LOCATION();
1447 m_deferredErrors = empty_vec_array();
1450 if (Logger::UseRequestLog) Logger::SetThreadHook(nullptr);
1451 if (m_requestTrace) record_trace(std::move(*m_requestTrace));
1454 template<class Action>
1455 static inline void enterVM(ActRec* ar, Action action) {
1456 enterVMCustomHandler(ar, [&] { exception_handler(action); });
1460 * Shared implementation for invokeFunc{,Few}().
1462 * The `doEnterVM' callback take an ActRec* argument corresponding to
1463 * the reentry frame.
1465 template<class FEnterVM>
1466 ALWAYS_INLINE
1467 TypedValue ExecutionContext::invokeFuncImpl(const Func* f,
1468 ObjectData* thiz, Class* cls,
1469 uint32_t numArgsInclUnpack,
1470 FEnterVM doEnterVM) {
1471 assertx(f);
1472 // If `f' is a regular function, `thiz' and `cls' must be null.
1473 assertx(IMPLIES(!f->implCls(), (!thiz && !cls)));
1474 // If `f' is a method, either `thiz' or `cls' must be non-null.
1475 assertx(IMPLIES(f->preClass(), thiz || cls));
1476 // If `f' is a static method, thiz must be null.
1477 assertx(IMPLIES(f->isStaticInPrologue(), !thiz));
1479 if (thiz != nullptr) thiz->incRefCount();
1481 ActRec* ar = vmStack().indA(numArgsInclUnpack);
1482 ar->setReturnVMExit();
1483 ar->m_func = f;
1484 if (thiz) {
1485 ar->setThis(thiz);
1486 } else if (cls) {
1487 ar->setClass(cls);
1488 } else {
1489 ar->trashThis();
1491 ar->setNumArgs(numArgsInclUnpack);
1493 #ifdef HPHP_TRACE
1494 if (vmfp() == nullptr) {
1495 TRACE(1, "Reentry: enter %s(%p) from top-level\n",
1496 f->name()->data(), ar);
1497 } else {
1498 TRACE(1, "Reentry: enter %s(pc %p ar %p) from %s(%p)\n",
1499 f->name()->data(), vmpc(), ar,
1500 vmfp()->m_func ? vmfp()->m_func->name()->data()
1501 : "unknownBuiltin",
1502 vmfp());
1504 #endif
1506 auto const reentrySP =
1507 vmStack().top() + numArgsInclUnpack + kNumActRecCells + f->numInOutParams();
1508 pushVMState(reentrySP);
1509 SCOPE_EXIT {
1510 assert_flog(
1511 vmStack().top() == reentrySP,
1512 "vmsp() mismatch around reentry: before @ {}, after @ {}",
1513 reentrySP, vmStack().top()
1515 popVMState();
1518 enterVM(ar, [&] { doEnterVM(ar); });
1520 if (UNLIKELY(f->takesInOutParams())) {
1521 // This is OK (albeit ugly) since the return value should only be readable
1522 // from user code via e.g. call_user_func and we will tagTV the result from
1523 // the native wrapper and getting the correct frame pointer here (or from
1524 // the normal arrprov::tagFromPC) is awkward.
1525 ARRPROV_USE_RUNTIME_LOCATION();
1527 VArrayInit varr(f->numInOutParams() + 1);
1528 for (uint32_t i = 0; i < f->numInOutParams() + 1; ++i) {
1529 varr.append(*vmStack().topTV());
1530 vmStack().popC();
1532 auto arr = varr.toArray();
1533 return make_array_like_tv(arr.detach());
1534 } else {
1535 auto const retval = *vmStack().topTV();
1536 vmStack().discard();
1537 return retval;
1542 * Enter VM by calling action(), which invokes a function or resumes
1543 * an async function. The 'ar' argument points to an ActRec of the
1544 * invoked/resumed function.
1546 template<class Action>
1547 static inline void enterVMCustomHandler(ActRec* ar, Action action) {
1548 assertx(ar);
1549 assertx(!ar->sfp());
1550 assertx(isReturnHelper(reinterpret_cast<void*>(ar->m_savedRip)));
1551 assertx(ar->callOffset() == 0);
1553 vmFirstAR() = ar;
1554 vmJitCalledFrame() = nullptr;
1555 vmJitReturnAddr() = 0;
1557 action();
1559 while (vmpc()) {
1560 exception_handler(enterVMAtCurPC);
1564 TypedValue ExecutionContext::invokePseudoMain(const Func* f,
1565 ObjectData* thiz /* = NULL */,
1566 Class* cls /* = NULL */) {
1567 assertx(f->isPseudoMain());
1568 auto toMerge = f->unit();
1569 toMerge->merge();
1570 if (toMerge->isMergeOnly()) {
1571 Stats::inc(Stats::PseudoMain_Skipped);
1572 return make_tv<KindOfInt64>(1);
1575 Stats::inc(Stats::PseudoMain_Executed);
1576 VMRegAnchor _;
1578 // We must do a stack overflow check for leaf functions on re-entry,
1579 // because we won't have checked that the stack is deep enough for a
1580 // leaf function /after/ re-entry, and the prologue for the leaf
1581 // function will not make a check.
1582 if (f->isPhpLeafFn()) {
1583 // Check both the native stack and VM stack for overflow.
1584 checkStack(vmStack(), f, kNumActRecCells);
1585 } else {
1586 // invokePseudoMain() must always check the native stack for overflow no
1587 // matter what.
1588 checkNativeStack();
1591 // Reserve space for ActRec.
1592 for (auto i = kNumActRecCells; i > 0; --i) vmStack().pushUninit();
1594 auto const doEnterVM = [&] (ActRec* ar) {
1595 enterVMAtPseudoMain(ar);
1598 return invokeFuncImpl(f, thiz, cls, 0, doEnterVM);
1601 TypedValue ExecutionContext::invokeFunc(const Func* f,
1602 const Variant& args_,
1603 ObjectData* thiz /* = NULL */,
1604 Class* cls /* = NULL */,
1605 bool dynamic /* = true */,
1606 bool checkRefAnnot /* = false */,
1607 bool allowDynCallNoPointer
1608 /* = false */,
1609 Array&& reifiedGenerics
1610 /* = Array() */) {
1611 VMRegAnchor _;
1613 // We must do a stack overflow check for leaf functions on re-entry,
1614 // because we won't have checked that the stack is deep enough for a
1615 // leaf function /after/ re-entry, and the prologue for the leaf
1616 // function will not make a check.
1617 if (f->isPhpLeafFn() ||
1618 !(f->numParams() <= kStackCheckReenterPadding - kNumActRecCells)) {
1619 // Check both the native stack and VM stack for overflow, numParams
1620 // is already included in f->maxStackCells().
1621 checkStack(vmStack(), f, f->numInOutParams() + kNumActRecCells);
1622 } else {
1623 // invokeFunc() must always check the native stack for overflow no
1624 // matter what.
1625 checkNativeStack();
1628 // Reserve space for inout outputs and ActRec.
1629 for (auto i = f->numInOutParams(); i > 0; --i) vmStack().pushUninit();
1630 for (auto i = kNumActRecCells; i > 0; --i) vmStack().pushUninit();
1632 // Push arguments.
1633 auto const& args = *args_.asTypedValue();
1634 assertx(isContainerOrNull(args));
1635 auto numArgs = tvIsNull(args) ? 0 : getContainerSize(args);
1636 if (numArgs > 0) {
1637 assertx(isContainer(args));
1638 tvDup(args, *vmStack().allocC());
1639 numArgs = prepareUnpackArgs(f, 0, checkRefAnnot);
1642 // Caller checks.
1643 if (dynamic) callerDynamicCallChecks(f, allowDynCallNoPointer);
1645 auto const doEnterVM = [&] (ActRec* ar) {
1646 enterVMAtFunc(ar, std::move(reifiedGenerics), f->takesInOutParams(),
1647 dynamic, allowDynCallNoPointer);
1650 return invokeFuncImpl(f, thiz, cls, numArgs, doEnterVM);
1653 TypedValue ExecutionContext::invokeFuncFew(
1654 const Func* f,
1655 ExecutionContext::ThisOrClass thisOrCls,
1656 uint32_t numArgs,
1657 const TypedValue* argv,
1658 bool dynamic /* = true */,
1659 bool allowDynCallNoPointer
1660 /* = false */
1662 VMRegAnchor _;
1663 auto& stack = vmStack();
1665 // See comments in invokeFunc().
1666 if (f->isPhpLeafFn() ||
1667 !(numArgs <= kStackCheckReenterPadding - kNumActRecCells)) {
1668 checkStack(stack, f, f->numInOutParams() + kNumActRecCells);
1669 } else {
1670 checkNativeStack();
1673 // Reserve space for inout outputs and ActRec.
1674 for (auto i = f->numInOutParams(); i > 0; --i) stack.pushUninit();
1675 for (auto i = kNumActRecCells; i > 0; --i) stack.pushUninit();
1677 // Push non-variadic arguments.
1678 auto const numParams = f->numNonVariadicParams();
1679 for (auto i = 0; i < numArgs && i < numParams; ++i) {
1680 const TypedValue *from = &argv[i];
1681 TypedValue* to = stack.allocTV();
1682 tvDup(*from, *to);
1685 // Push variadic arguments.
1686 if (UNLIKELY(numParams < numArgs)) {
1687 VArrayInit ai{numArgs - numParams};
1688 for (auto i = numParams; i < numArgs; ++i) ai.append(argv[i]);
1689 if (RuntimeOption::EvalHackArrDVArrs) {
1690 stack.pushVecNoRc(ai.create());
1691 } else {
1692 stack.pushArrayNoRc(ai.create());
1694 numArgs = numParams + 1;
1697 // Caller checks.
1698 if (dynamic) callerDynamicCallChecks(f, allowDynCallNoPointer);
1700 auto const doEnterVM = [&] (ActRec* ar) {
1701 enterVMAtFunc(ar, Array(), f->takesInOutParams(), dynamic, false);
1704 return invokeFuncImpl(f,
1705 thisOrCls.left(),
1706 thisOrCls.right(),
1707 numArgs, doEnterVM);
1710 static void prepareAsyncFuncEntry(ActRec* enterFnAr,
1711 Resumable* resumable,
1712 bool willUnwind) {
1713 assertx(enterFnAr);
1714 assertx(enterFnAr->func()->isAsync());
1715 assertx(isResumed(enterFnAr));
1716 assertx(resumable);
1718 vmfp() = enterFnAr;
1719 // If we're going to unwind, we need to resume at the offset we
1720 // suspended at, so we look up the correct catch trace (not the one
1721 // for the next op).
1722 vmpc() = enterFnAr->func()->unit()->at(
1723 willUnwind
1724 ? resumable->suspendOffset()
1725 : resumable->resumeFromAwaitOffset()
1727 EventHook::FunctionResumeAwait(enterFnAr);
1730 void ExecutionContext::resumeAsyncFunc(Resumable* resumable,
1731 ObjectData* freeObj,
1732 const TypedValue awaitResult) {
1733 assertx(tl_regState == VMRegState::CLEAN);
1734 SCOPE_EXIT { assertx(tl_regState == VMRegState::CLEAN); };
1736 auto fp = resumable->actRec();
1738 if (RO::EvalEnableImplicitContext) {
1739 *ImplicitContext::activeCtx = [&] {
1740 if (!fp->func()->isGenerator()) return frame_afwh(fp)->m_implicitContext;
1741 auto gen = frame_async_generator(fp);
1742 return gen->getWaitHandle()->m_implicitContext;
1743 }();
1746 // We don't need to check for space for the ActRec (unlike generally
1747 // in normal re-entry), because the ActRec isn't on the stack.
1748 checkStack(vmStack(), fp->func(), 0);
1750 TypedValue* savedSP = vmStack().top();
1751 tvDup(awaitResult, *vmStack().allocC());
1753 // decref after awaitResult is on the stack
1754 decRefObj(freeObj);
1756 pushVMState(savedSP);
1757 SCOPE_EXIT { popVMState(); };
1759 enterVM(fp, [&] {
1760 prepareAsyncFuncEntry(fp, resumable, false);
1762 const bool useJit = RID().getJit();
1763 if (LIKELY(useJit && resumable->resumeAddr())) {
1764 Stats::inc(Stats::VMEnter);
1765 jit::enterTC(resumable->resumeAddr());
1766 } else {
1767 enterVMAtCurPC();
1772 void ExecutionContext::resumeAsyncFuncThrow(Resumable* resumable,
1773 ObjectData* freeObj,
1774 ObjectData* exception) {
1775 assertx(exception);
1776 assertx(exception->instanceof(SystemLib::s_ThrowableClass));
1777 assertx(tl_regState == VMRegState::CLEAN);
1778 SCOPE_EXIT { assertx(tl_regState == VMRegState::CLEAN); };
1780 auto fp = resumable->actRec();
1782 if (RO::EvalEnableImplicitContext) {
1783 *ImplicitContext::activeCtx = [&] {
1784 if (!fp->func()->isGenerator()) return frame_afwh(fp)->m_implicitContext;
1785 auto gen = frame_async_generator(fp);
1786 return gen->getWaitHandle()->m_implicitContext;
1787 }();
1790 checkStack(vmStack(), fp->func(), 0);
1792 // decref after we hold reference to the exception
1793 Object e(exception);
1794 decRefObj(freeObj);
1796 pushVMState(vmStack().top());
1797 SCOPE_EXIT { popVMState(); };
1799 enterVMCustomHandler(fp, [&] {
1800 prepareAsyncFuncEntry(fp, resumable, true);
1801 unwindVM(exception);
1802 e.reset();
1806 ActRec* ExecutionContext::getPrevVMState(const ActRec* fp,
1807 Offset* prevPc /* = NULL */,
1808 TypedValue** prevSp /* = NULL */,
1809 bool* fromVMEntry /* = NULL */,
1810 uint64_t* jitReturnAddr /* = NULL */) {
1811 if (fp == nullptr) {
1812 return nullptr;
1814 ActRec* prevFp = fp->sfp();
1815 if (LIKELY(prevFp != nullptr)) {
1816 if (prevSp) {
1817 if (UNLIKELY(isResumed(fp))) {
1818 assertx(fp->func()->isGenerator());
1819 *prevSp = (TypedValue*)prevFp - prevFp->func()->numSlotsInFrame();
1820 } else {
1821 *prevSp = (TypedValue*)(fp + 1);
1824 if (prevPc) *prevPc = prevFp->func()->base() + fp->callOffset();
1825 if (fromVMEntry) *fromVMEntry = false;
1826 return prevFp;
1828 // Linear search from end of m_nestedVMs. In practice, we're probably
1829 // looking for something recently pushed.
1830 int i = m_nestedVMs.size() - 1;
1831 ActRec* firstAR = vmFirstAR();
1832 while (i >= 0 && firstAR != fp) {
1833 firstAR = m_nestedVMs[i--].firstAR;
1835 if (i == -1) return nullptr;
1836 const VMState& vmstate = m_nestedVMs[i];
1837 prevFp = vmstate.fp;
1838 assertx(prevFp);
1839 assertx(prevFp->func()->unit());
1840 if (prevSp) *prevSp = vmstate.sp;
1841 if (prevPc) {
1842 *prevPc = prevFp->func()->unit()->offsetOf(vmstate.pc);
1844 if (fromVMEntry) *fromVMEntry = true;
1845 if (jitReturnAddr) *jitReturnAddr = (uint64_t)vmstate.jitReturnAddr;
1846 return prevFp;
1850 Instantiate hoistable classes and functions.
1851 If there is any more work left to do, setup a
1852 new frame ready to execute the pseudomain.
1854 return true iff the pseudomain needs to be executed.
1856 bool ExecutionContext::evalUnit(Unit* unit, PC callPC, PC& pc, int funcType) {
1857 vmpc() = callPC;
1858 unit->merge();
1859 if (unit->isMergeOnly()) {
1860 Stats::inc(Stats::PseudoMain_Skipped);
1861 *vmStack().allocTV() = make_tv<KindOfInt64>(1);
1862 return false;
1865 Stats::inc(Stats::PseudoMain_Executed);
1867 ActRec* ar = vmStack().allocA();
1868 auto const cls = vmfp()->func()->cls();
1869 auto const hasThis = cls && !vmfp()->func()->isStatic();
1870 auto const func = unit->getMain(cls, hasThis);
1871 assertx(!func->isCPPBuiltin());
1872 ar->m_func = func;
1873 if (cls) {
1874 ar->setThisOrClass(vmfp()->getThisOrClass());
1875 if (hasThis) ar->getThis()->incRefCount();
1876 } else {
1877 ar->trashThis();
1879 ar->setNumArgs(0);
1880 assertx(vmfp());
1881 ar->setReturn(vmfp(), callPC, jit::tc::ustubs().retHelper, false);
1882 pushFrameSlots(func);
1884 auto prevFp = vmfp();
1885 if (UNLIKELY(prevFp->skipFrame())) {
1886 prevFp = g_context->getPrevVMStateSkipFrame(prevFp);
1888 assertx(prevFp);
1890 vmfp() = ar;
1891 pc = func->getEntry();
1892 vmpc() = pc;
1893 bool ret = EventHook::FunctionCall(vmfp(), funcType);
1894 pc = vmpc();
1895 checkStack(vmStack(), func, 0);
1896 return ret;
1899 Variant ExecutionContext::getEvaledArg(const StringData* val,
1900 const String& namespacedName,
1901 const Unit* funcUnit) {
1902 auto key = StrNR(val);
1904 if (m_evaledArgs.get()) {
1905 auto const arg = m_evaledArgs.get()->get(key);
1906 if (arg.is_init()) return Variant::wrap(arg);
1909 String code;
1910 String funcName;
1911 int pos = namespacedName.rfind('\\');
1912 if (pos != -1) {
1913 auto ns = namespacedName.substr(0, pos);
1914 code = s_hh + s_namespace + ns + s_curly_start + s_function_start +
1915 s_evaluate_default_argument + s_function_middle + key +
1916 s_semicolon + s_curly_end + s_curly_end;
1917 funcName = ns + "\\" + s_evaluate_default_argument;
1918 } else {
1919 code = s_hh + s_function_start + s_evaluate_default_argument +
1920 s_function_middle + key + s_semicolon + s_curly_end;
1921 funcName = s_evaluate_default_argument;
1924 Unit* unit = compileEvalString(code.get());
1925 assertx(unit != nullptr);
1926 unit->setInterpretOnly();
1928 // The evaluate_default_argument function should be the last one
1929 auto func = *(unit->funcs().end() - 1);
1930 assertx(func->name()->equal(funcName.get()) &&
1931 "We expecting the evaluate_default_argument func");
1933 // Default arg values are not currently allowed to depend on class context.
1934 auto v = Variant::attach(
1935 g_context->invokeFuncFew(func, nullptr, 0, nullptr, true, true)
1937 auto const lv = m_evaledArgs.lvalForce(key, AccessFlags::Key);
1938 tvSet(*v.asTypedValue(), lv);
1939 return Variant::wrap(lv.tv());
1942 void ExecutionContext::recordLastError(const Exception& e, int errnum) {
1943 m_lastError = String(e.getMessage());
1944 m_lastErrorNum = errnum;
1945 m_lastErrorPath = String::attach(getContainingFileName());
1946 m_lastErrorLine = getLine();
1947 if (auto const ee = dynamic_cast<const ExtendedException*>(&e)) {
1948 m_lastErrorPath = ee->getFileAndLine().first;
1949 m_lastErrorLine = ee->getFileAndLine().second;
1953 void ExecutionContext::clearLastError() {
1954 m_lastError = String();
1955 m_lastErrorNum = 0;
1956 m_lastErrorPath = staticEmptyString();
1957 m_lastErrorLine = 0;
1960 void ExecutionContext::enqueueAPCDeferredExpire(const String& key) {
1961 auto keyStr = key.get();
1962 keyStr->incRefCount();
1963 m_apcDeferredExpire.push_back(keyStr);
1966 void ExecutionContext::enqueueAPCHandle(APCHandle* handle, size_t size) {
1967 assertx(handle->isUncounted());
1968 m_apcHandles.push_back(handle);
1969 m_apcMemSize += size;
1972 // Treadmill solution for the SharedVariant memory management
1973 namespace {
1974 struct FreedAPCHandle {
1975 explicit FreedAPCHandle(std::vector<APCHandle*>&& shandles, size_t size)
1976 : m_memSize(size), m_apcHandles(std::move(shandles))
1978 void operator()() {
1979 for (auto handle : m_apcHandles) {
1980 APCTypedValue::fromHandle(handle)->deleteUncounted();
1982 APCStats::getAPCStats().removePendingDelete(m_memSize);
1984 private:
1985 size_t m_memSize;
1986 std::vector<APCHandle*> m_apcHandles;
1990 void ExecutionContext::manageAPCHandle() {
1991 assertx(apcExtension::UseUncounted || m_apcHandles.size() == 0);
1992 if (m_apcHandles.size() > 0) {
1993 Treadmill::enqueue(
1994 FreedAPCHandle(std::move(m_apcHandles), m_apcMemSize)
1996 APCStats::getAPCStats().addPendingDelete(m_apcMemSize);
2000 // Evaled units have a footprint in the TC and translation metadata. The
2001 // applications we care about tend to have few, short, stereotyped evals,
2002 // where the same code keeps getting eval'ed over and over again; so we
2003 // keep around units for each eval'ed string, so that the TC space isn't
2004 // wasted on each eval.
2005 typedef RankedCHM<StringData*, HPHP::Unit*,
2006 StringDataHashCompare,
2007 RankEvaledUnits> EvaledUnitsMap;
2008 static EvaledUnitsMap s_evaledUnits;
2009 Unit* ExecutionContext::compileEvalString(
2010 StringData* code,
2011 const char* evalFilename /* = nullptr */) {
2012 EvaledUnitsMap::accessor acc;
2013 // Promote this to a static string; otherwise it may get swept
2014 // across requests.
2015 code = makeStaticString(code);
2016 if (s_evaledUnits.insert(acc, code)) {
2017 acc->second = compile_string(
2018 code->data(),
2019 code->size(),
2020 evalFilename,
2021 Native::s_noNativeFuncs,
2022 getRepoOptionsForCurrentFrame()
2025 return acc->second;
2028 ExecutionContext::EvaluationResult
2029 ExecutionContext::evalPHPDebugger(StringData* code, int frame) {
2030 // The code has "<?hh" prepended already
2031 auto unit = compile_debugger_string(code->data(), code->size(),
2032 getRepoOptionsForCurrentFrame());
2033 if (unit == nullptr) {
2034 raise_error("Syntax error");
2035 return {true, init_null_variant, "Syntax error"};
2038 return evalPHPDebugger(unit, frame);
2041 const StaticString
2042 s_DebuggerMainAttr("__DebuggerMain"),
2043 s_uninitClsName("__uninitSentinel");
2045 ExecutionContext::EvaluationResult
2046 ExecutionContext::evalPHPDebugger(Unit* unit, int frame) {
2047 always_assert(!RuntimeOption::RepoAuthoritative);
2049 // Do not JIT this unit, we are using it exactly once.
2050 unit->setInterpretOnly();
2052 VMRegAnchor _;
2054 auto fp = getFrameAtDepthForDebuggerUnsafe(frame);
2056 // Continue walking up the stack until we find a frame that can have
2057 // a variable environment context attached to it, or we run out out frames.
2058 while (fp && (fp->skipFrame() || fp->isInlined())) {
2059 fp = getPrevVMStateSkipFrame(fp);
2062 ObjectData *this_ = nullptr;
2063 // NB: the ActRec and function within the AR may have different classes. The
2064 // class in the ActRec is the type used when invoking the function (i.e.,
2065 // Derived in Derived::Foo()) while the class obtained from the function is
2066 // the type that declared the function Foo, which may be Base. We need both
2067 // the class to match any object that this function may have been invoked on,
2068 // and we need the class from the function execution is stopped in.
2069 Class *frameClass = nullptr;
2070 Class *functionClass = nullptr;
2071 if (fp) {
2072 functionClass = fp->m_func->cls();
2073 if (functionClass) {
2074 if (fp->hasThis()) {
2075 this_ = fp->getThis();
2076 } else if (fp->hasClass()) {
2077 frameClass = fp->getClass();
2080 phpDebuggerEvalHook(fp->m_func);
2083 const static StaticString s_cppException("Hit an exception");
2084 const static StaticString s_phpException("Hit a php exception");
2085 const static StaticString s_exit("Hit exit");
2086 const static StaticString s_fatal("Hit fatal");
2087 std::ostringstream errorString;
2088 std::string stack;
2090 // Find a suitable PC to use when switching to the target frame. If the target
2091 // is the current frame, this is just vmpc(). For other cases, this will
2092 // generally be the address of a call from that frame's function. If we can't
2093 // find the target frame (because it lies deeper in the stack), then just use
2094 // the target frame's func's entry point.
2095 auto const findSuitablePC = [this](const ActRec* target){
2096 if (auto fp = vmfp()) {
2097 if (fp == target) return vmpc();
2098 while (true) {
2099 auto prevFp = getPrevVMState(fp);
2100 if (!prevFp) break;
2101 if (prevFp == target) {
2102 return prevFp->func()->getEntry() + fp->callOffset();
2104 fp = prevFp;
2107 return target->func()->getEntry();
2110 try {
2111 // Start with the correct parent FP so that VarEnv can properly exitFP().
2112 // Note that if the same VarEnv is used across multiple frames, the most
2113 // recent FP must be used. This can happen if we are trying to debug
2114 // an eval() call or a call issued by debugger itself.
2116 // We also need to change vmpc() to match, since we assert in a few places
2117 // that the vmpc() lies within vmfp()'s code.
2118 auto savedFP = vmfp();
2119 auto savedPC = vmpc();
2120 if (fp) {
2121 vmpc() = findSuitablePC(fp);
2122 vmfp() = fp;
2124 SCOPE_EXIT { vmpc() = savedPC; vmfp() = savedFP; };
2126 invokePseudoMain(
2127 unit->getMain(functionClass, this_),
2128 this_,
2129 frameClass
2132 enum VarAction { StoreFrame, StoreEnv };
2134 auto const uninit_cls = Unit::loadClass(s_uninitClsName.get());
2136 auto& env = m_debuggerEnv;
2138 auto const ctx = fp ? fp->func()->cls() : nullptr;
2139 auto const f = [&] () -> Func* {
2140 for (auto orig_f : unit->funcs()) {
2141 if (orig_f->userAttributes().count(s_DebuggerMainAttr.get())) {
2142 auto const f = ctx ? orig_f->clone(ctx) : orig_f;
2143 if (ctx) {
2144 f->setNewFuncId();
2145 f->setBaseCls(ctx);
2146 if (fp && fp->hasThis()) {
2147 f->setAttrs(Attr(f->attrs() & ~AttrStatic));
2148 } else if (fp && fp->hasClass()) {
2149 f->setAttrs(Attr(f->attrs() | AttrStatic));
2152 assertx(f->numParams() >= 1 && f->numParams() == f->numInOutParams());
2153 return f;
2156 return nullptr;
2157 }();
2158 always_assert(f);
2160 VArrayInit args{f->numParams()};
2161 std::vector<VarAction> actions{f->numParams(), StoreEnv};
2162 std::vector<Id> frameIds;
2163 frameIds.resize(f->numParams(), 0);
2164 auto const appendUninit = [&] {
2165 args.append(make_tv<KindOfObject>(Object{uninit_cls}.detach()));
2167 for (Id id = 0; id < f->numParams() - 1; id++) {
2168 assertx(id < f->numNamedLocals());
2169 assertx(f->params()[id].isInOut());
2170 if (fp) {
2171 auto const idx = fp->func()->lookupVarId(f->localVarName(id));
2172 if (idx != kInvalidId) {
2173 Variant var{tvAsCVarRef(*frame_local(fp, idx))};
2174 if (var.isInitialized()) args.append(var);
2175 else appendUninit();
2176 actions[id] = StoreFrame;
2177 frameIds[id] = idx;
2178 continue;
2181 auto const val = env.lookup(StrNR{f->localVarName(id)});
2182 if (val.is_init()) args.append(val);
2183 else appendUninit();
2185 args.append(make_tv<KindOfNull>()); // $__debugger_exn$output
2187 auto const obj = ctx && fp->hasThis() ? fp->getThis() : nullptr;
2188 auto const cls = ctx && fp->hasClass() ? fp->getClass() : nullptr;
2189 auto const arr_tv = invokeFunc(f, args.toArray(), obj, cls, false);
2190 assertx(isArrayLikeType(type(arr_tv)));
2191 assertx(val(arr_tv).parr->size() == f->numParams() + 1);
2192 Array arr = Array::attach(val(arr_tv).parr);
2193 for (Id id = 0; id < f->numParams() - 1; id++) {
2194 auto const tv = arr.lookup(id + 1);
2195 if (isObjectType(type(tv)) &&
2196 val(tv).pobj->instanceof(uninit_cls)) {
2197 switch (actions[id]) {
2198 case StoreFrame:
2199 variant_ref{frame_local(fp, frameIds[id])}.unset();
2200 break;
2201 case StoreEnv:
2202 env.remove(StrNR{f->localVarName(id)});
2203 break;
2205 continue;
2207 switch (actions[id]) {
2208 case StoreFrame:
2209 variant_ref{frame_local(fp, frameIds[id])} = arr[id + 1];
2210 break;
2211 case StoreEnv:
2212 env.set(StrNR{f->localVarName(id)}, tv);
2213 break;
2216 auto const exn = arr[int64_t(f->numParams())];
2217 if (exn.isObject()) {
2218 assertx(exn.toObject().instanceof("Throwable"));
2219 throw_object(exn.toObject());
2221 assertx(exn.isNull());
2223 return {false, arr[0], ""};
2224 } catch (FatalErrorException& e) {
2225 errorString << s_fatal.data();
2226 errorString << " : ";
2227 errorString << e.getMessage().c_str();
2228 errorString << "\n";
2229 stack = ExtendedLogger::StringOfStackTrace(e.getBacktrace());
2230 } catch (ExitException& e) {
2231 errorString << s_exit.data();
2232 errorString << " : ";
2233 errorString << *rl_exit_code;
2234 } catch (Eval::DebuggerException& e) {
2235 } catch (Exception& e) {
2236 errorString << s_cppException.data();
2237 errorString << " : ";
2238 errorString << e.getMessage().c_str();
2239 ExtendedException* ee = dynamic_cast<ExtendedException*>(&e);
2240 if (ee) {
2241 errorString << "\n";
2242 stack = ExtendedLogger::StringOfStackTrace(ee->getBacktrace());
2244 } catch (Object &e) {
2245 errorString << s_phpException.data();
2246 errorString << " : ";
2247 try {
2248 errorString << throwable_to_string(e.get()).data();
2249 } catch (...) {
2250 errorString << e->getVMClass()->name()->data();
2252 } catch (...) {
2253 errorString << s_cppException.data();
2256 auto errorStr = errorString.str();
2257 g_context->write(errorStr);
2258 if (!stack.empty()) {
2259 g_context->write(stack.c_str());
2262 return {true, init_null_variant, errorStr};
2265 void ExecutionContext::enterDebuggerDummyEnv() {
2266 static Unit* s_debuggerDummy = compile_debugger_string(
2267 "<?hh", 4, RepoOptions::defaults()
2269 // Ensure that the VM stack is completely empty (vmfp() should be null)
2270 // and that we're not in a nested VM (reentrancy)
2271 assertx(vmfp() == nullptr);
2272 assertx(m_nestedVMs.size() == 0);
2273 assertx(m_nesting == 0);
2274 assertx(vmStack().count() == 0);
2275 ActRec* ar = vmStack().allocA();
2276 ar->m_func = s_debuggerDummy->getMain(nullptr, false);
2277 ar->setNumArgs(0);
2278 ar->trashThis();
2279 ar->setReturnVMExit();
2280 vmfp() = ar;
2281 vmpc() = s_debuggerDummy->entry();
2282 vmFirstAR() = ar;
2285 void ExecutionContext::exitDebuggerDummyEnv() {
2286 assertx(m_globalNVTable);
2287 // Ensure that vmfp() is valid
2288 assertx(vmfp() != nullptr);
2289 // Ensure that vmfp() points to the only frame on the call stack.
2290 // In other words, make sure there are no VM frames directly below
2291 // this one and that we are not in a nested VM (reentrancy)
2292 assertx(!vmfp()->sfp());
2293 assertx(m_nestedVMs.size() == 0);
2294 assertx(m_nesting == 0);
2295 // Teardown the frame we erected by enterDebuggerDummyEnv()
2296 const Func* func = vmfp()->m_func;
2297 try {
2298 vmfp()->setLocalsDecRefd();
2299 frame_free_locals_no_hook(vmfp());
2300 } catch (...) {}
2301 vmStack().ndiscard(func->numSlotsInFrame());
2302 vmStack().discardAR();
2303 // After tearing down this frame, the VM stack should be completely empty
2304 assertx(vmStack().count() == 0);
2305 vmfp() = nullptr;
2306 vmpc() = nullptr;
2309 ThrowAllErrorsSetter::ThrowAllErrorsSetter() {
2310 m_throwAllErrors = g_context->getThrowAllErrors();
2311 g_context->setThrowAllErrors(true);
2314 ThrowAllErrorsSetter::~ThrowAllErrorsSetter() {
2315 g_context->setThrowAllErrors(m_throwAllErrors);