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