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