Remove String::operator const char*().
[hiphop-php.git] / hphp / runtime / base / execution_context.cpp
blobf260ad53df0a1bf39205eb02ea6a0e8c1792212a
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- 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 +----------------------------------------------------------------------+
17 #define __STDC_LIMIT_MACROS
18 #include <stdint.h>
20 #include <runtime/base/execution_context.h>
21 #include <runtime/base/complex_types.h>
22 #include <runtime/base/type_conversions.h>
23 #include <runtime/base/builtin_functions.h>
24 #include <runtime/base/comparisons.h>
25 #include <runtime/base/externals.h>
26 #include <runtime/base/util/request_local.h>
27 #include <runtime/base/resource_data.h>
28 #include <runtime/base/array/array_init.h>
29 #include <runtime/base/array/array_iterator.h>
30 #include <runtime/base/memory/memory_manager.h>
31 #include <runtime/base/memory/sweepable.h>
32 #include <runtime/base/runtime_option.h>
33 #include <runtime/eval/debugger/debugger.h>
34 #include <runtime/vm/event_hook.h>
35 #include <runtime/ext/ext_string.h>
36 #include <util/logger.h>
37 #include <util/process.h>
38 #include <util/text_color.h>
39 #include <runtime/eval/runtime/file_repository.h>
40 #include <runtime/vm/translator/translator.h>
41 #include <runtime/vm/translator/translator-inline.h>
42 #include <runtime/vm/translator/translator-deps.h>
43 #include <runtime/vm/debugger_hook.h>
44 #include <runtime/base/server/server_stats.h>
46 namespace HPHP {
47 ///////////////////////////////////////////////////////////////////////////////
49 IMPLEMENT_THREAD_LOCAL_NO_CHECK_HOT(ExecutionContext, g_context);
51 int64_t VMExecutionContext::s_threadIdxCounter = 0;
52 Mutex VMExecutionContext::s_threadIdxLock;
53 hphp_hash_map<pid_t, int64_t> VMExecutionContext::s_threadIdxMap;
55 BaseExecutionContext::BaseExecutionContext() :
56 m_fp(nullptr), m_pc(nullptr), m_isValid(1), m_eventHook(nullptr),
57 m_transport(nullptr),
58 m_maxTime(RuntimeOption::RequestTimeoutSeconds),
59 m_cwd(Process::CurrentWorkingDirectory),
60 m_out(nullptr), m_implicitFlush(false), m_protectedLevel(0),
61 m_stdout(nullptr), m_stdoutData(nullptr),
62 m_errorState(ExecutionContext::NoError),
63 m_errorReportingLevel(RuntimeOption::RuntimeErrorReportingLevel),
64 m_lastErrorNum(0), m_logErrors(false), m_throwAllErrors(false),
65 m_vhost(nullptr) {
67 setRequestMemoryMaxBytes(RuntimeOption::RequestMemoryMaxBytes);
68 m_include_paths = Array::Create();
69 for (unsigned int i = 0; i < RuntimeOption::IncludeSearchPaths.size(); ++i) {
70 m_include_paths.append(String(RuntimeOption::IncludeSearchPaths[i]));
74 VMExecutionContext::VMExecutionContext() :
75 m_lambdaCounter(0), m_nesting(0),
76 m_injTables(nullptr), m_breakPointFilter(nullptr), m_lastLocFilter(nullptr),
77 m_interpreting(false), m_dbgNoBreak(false),
78 m_coverPrevLine(-1), m_coverPrevUnit(nullptr),
79 m_executingSetprofileCallback(false) {
81 // Make sure any fields accessed from the TC are within a byte of
82 // ExecutionContext's beginning.
83 static_assert(offsetof(ExecutionContext, m_stack) <= 0xff,
84 "m_stack offset too large");
85 static_assert(offsetof(ExecutionContext, m_fp) <= 0xff,
86 "m_fp offset too large");
87 static_assert(offsetof(ExecutionContext, m_pc) <= 0xff,
88 "m_pc offset too large");
89 static_assert(offsetof(ExecutionContext, m_isValid) <= 0xff,
90 "m_isValid offset too large");
91 static_assert(offsetof(ExecutionContext, m_eventHook) <= 0xff,
92 "m_eventHook offset too large");
93 static_assert(offsetof(ExecutionContext, m_currentThreadIdx) <= 0xff,
94 "m_currentThreadIdx offset too large");
97 Lock lock(s_threadIdxLock);
98 pid_t tid = Process::GetThreadPid();
99 if (!mapGet(s_threadIdxMap, tid, &m_currentThreadIdx)) {
100 m_currentThreadIdx = s_threadIdxCounter++;
101 s_threadIdxMap[tid] = m_currentThreadIdx;
104 m_eventHook = new HPHP::VM::EventHook();
107 BaseExecutionContext::~BaseExecutionContext() {
108 obFlushAll();
109 for (std::list<OutputBuffer*>::const_iterator iter = m_buffers.begin();
110 iter != m_buffers.end(); ++iter) {
111 delete *iter;
115 VMExecutionContext::~VMExecutionContext() {
116 // Discard any ConstInfo objects that were created to support reflection.
117 for (ConstInfoMap::const_iterator it = m_constInfo.begin();
118 it != m_constInfo.end(); ++it) {
119 delete it->second;
121 // decRef all of the PhpFiles in m_evaledFiles. Any PhpFile whose refcount
122 // reaches zero will be destroyed. Currently each PhpFile "owns" its Unit,
123 // so when a PhpFile is destroyed it will free its Unit as well.
124 for (EvaledFilesMap::iterator it = m_evaledFiles.begin();
125 it != m_evaledFiles.end();) {
126 EvaledFilesMap::iterator current = it;
127 ++it;
128 StringData* sd = current->first;
129 Eval::PhpFile* efile = current->second;
130 efile->decRefAndDelete();
131 m_evaledFiles.erase(current);
132 decRefStr(sd);
134 // Discard all units that were created via create_function().
135 for (EvaledUnitsVec::iterator it = m_createdFuncs.begin();
136 it != m_createdFuncs.end(); ++it) {
137 delete *it;
140 delete m_eventHook;
141 delete m_injTables;
142 delete m_breakPointFilter;
143 delete m_lastLocFilter;
145 if (UNLIKELY(!m_preConsts.empty())) {
146 VM::Transl::unmergePreConsts(m_preConsts, this);
147 for (VM::PreConstVec::iterator i = m_preConsts.begin();
148 i != m_preConsts.end(); ++i) {
149 decRefStr(const_cast<StringData*>(i->name));
154 void BaseExecutionContext::backupSession() {
155 m_shutdownsBackup = m_shutdowns;
156 m_userErrorHandlersBackup = m_userErrorHandlers;
157 m_userExceptionHandlersBackup = m_userExceptionHandlers;
160 void BaseExecutionContext::restoreSession() {
161 m_shutdowns = m_shutdownsBackup;
162 m_userErrorHandlers = m_userErrorHandlersBackup;
163 m_userExceptionHandlers = m_userExceptionHandlersBackup;
166 ///////////////////////////////////////////////////////////////////////////////
167 // system functions
169 String BaseExecutionContext::getMimeType() const {
170 String mimetype;
171 if (m_transport) {
172 mimetype = m_transport->getMimeType();
175 if (strncasecmp(mimetype.data(), "text/", 5) == 0) {
176 int pos = mimetype.find(';');
177 if (pos != String::npos) {
178 mimetype = mimetype.substr(0, pos);
180 } else if (m_transport && m_transport->sendDefaultContentType()) {
181 mimetype = m_transport->getDefaultContentType();
183 return mimetype;
186 std::string BaseExecutionContext::getRequestUrl(size_t szLimit) {
187 Transport* t = getTransport();
188 std::string ret = t ? t->getUrl() : "";
189 if (szLimit != std::string::npos) {
190 ret = ret.substr(0, szLimit);
192 return ret;
195 void BaseExecutionContext::setContentType(CStrRef mimetype, CStrRef charset) {
196 if (m_transport) {
197 String contentType = mimetype;
198 contentType += "; ";
199 contentType += "charset=";
200 contentType += charset;
201 m_transport->addHeader("Content-Type", contentType.c_str());
202 m_transport->setDefaultContentType(false);
206 void BaseExecutionContext::setRequestMemoryMaxBytes(int64_t max) {
207 if (max <= 0) {
208 max = INT64_MAX;
210 m_maxMemory = max;
211 MemoryManager::TheMemoryManager()->getStats().maxBytes = m_maxMemory;
214 ///////////////////////////////////////////////////////////////////////////////
215 // write()
217 void BaseExecutionContext::write(CStrRef s) {
218 write(s.data(), s.size());
221 void BaseExecutionContext::setStdout(PFUNC_STDOUT func, void *data) {
222 m_stdout = func;
223 m_stdoutData = data;
226 static void safe_stdout(const void *ptr, size_t size) {
227 write(fileno(stdout), ptr, size);
230 void BaseExecutionContext::writeStdout(const char *s, int len) {
231 if (m_stdout == nullptr) {
232 if (s_stdout_color) {
233 safe_stdout(s_stdout_color, strlen(s_stdout_color));
234 safe_stdout(s, len);
235 safe_stdout(ANSI_COLOR_END, strlen(ANSI_COLOR_END));
236 } else {
237 safe_stdout(s, len);
239 } else {
240 m_stdout(s, len, m_stdoutData);
244 void BaseExecutionContext::write(const char *s, int len) {
245 if (m_out) {
246 m_out->append(s, len);
247 } else {
248 writeStdout(s, len);
250 if (m_implicitFlush) flush();
253 ///////////////////////////////////////////////////////////////////////////////
254 // output buffers
256 void BaseExecutionContext::obProtect(bool on) {
257 m_protectedLevel = on ? m_buffers.size() : 0;
260 void BaseExecutionContext::obStart(CVarRef handler /* = null */) {
261 OutputBuffer *ob = new OutputBuffer();
262 ob->handler = handler;
263 m_buffers.push_back(ob);
264 resetCurrentBuffer();
267 String BaseExecutionContext::obCopyContents() {
268 if (!m_buffers.empty()) {
269 StringBuffer &oss = m_buffers.back()->oss;
270 if (!oss.empty()) {
271 return oss.copy();
274 return "";
277 String BaseExecutionContext::obDetachContents() {
278 if (!m_buffers.empty()) {
279 StringBuffer &oss = m_buffers.back()->oss;
280 if (!oss.empty()) {
281 return oss.detach();
284 return "";
287 int BaseExecutionContext::obGetContentLength() {
288 if (m_buffers.empty()) {
289 return 0;
291 return m_buffers.back()->oss.size();
294 void BaseExecutionContext::obClean() {
295 if (!m_buffers.empty()) {
296 m_buffers.back()->oss.reset();
300 bool BaseExecutionContext::obFlush() {
301 assert(m_protectedLevel >= 0);
302 if ((int)m_buffers.size() > m_protectedLevel) {
303 std::list<OutputBuffer*>::const_iterator iter = m_buffers.end();
304 OutputBuffer *last = *(--iter);
305 const int flag = PHP_OUTPUT_HANDLER_START | PHP_OUTPUT_HANDLER_END;
306 if (iter != m_buffers.begin()) {
307 OutputBuffer *prev = *(--iter);
308 if (last->handler.isNull()) {
309 prev->oss.absorb(last->oss);
310 } else {
311 try {
312 Variant tout =
313 vm_call_user_func(last->handler,
314 CREATE_VECTOR2(last->oss.detach(), flag));
315 prev->oss.append(tout.toString());
316 last->oss.reset();
317 } catch (...) {
318 prev->oss.absorb(last->oss);
321 return true;
324 if (!last->handler.isNull()) {
325 try {
326 Variant tout =
327 vm_call_user_func(last->handler,
328 CREATE_VECTOR2(last->oss.detach(), flag));
329 String sout = tout.toString();
330 writeStdout(sout.data(), sout.size());
331 last->oss.reset();
332 return true;
333 } catch (...) {}
336 writeStdout(last->oss.data(), last->oss.size());
337 last->oss.reset();
338 return true;
340 return false;
343 void BaseExecutionContext::obFlushAll() {
344 while (obFlush()) { obEnd();}
347 bool BaseExecutionContext::obEnd() {
348 assert(m_protectedLevel >= 0);
349 if ((int)m_buffers.size() > m_protectedLevel) {
350 delete m_buffers.back();
351 m_buffers.pop_back();
352 resetCurrentBuffer();
353 if (m_implicitFlush) flush();
354 return true;
356 if (m_implicitFlush) flush();
357 return false;
360 void BaseExecutionContext::obEndAll() {
361 while (obEnd()) {}
364 int BaseExecutionContext::obGetLevel() {
365 assert((int)m_buffers.size() >= m_protectedLevel);
366 return m_buffers.size() - m_protectedLevel;
369 static const StaticString s_level("level");
370 static const StaticString s_type("type");
371 static const StaticString s_name("name");
372 static const StaticString s_args("args");
373 static const StaticString s_default_output_handler("default output handler");
375 Array BaseExecutionContext::obGetStatus(bool full) {
376 Array ret = Array::Create();
377 std::list<OutputBuffer*>::const_iterator iter = m_buffers.begin();
378 ++iter; // skip over the fake outermost buffer
379 int level = 0;
380 for (; iter != m_buffers.end(); ++iter, ++level) {
381 Array status;
382 status.set(s_level, level);
383 if (level < m_protectedLevel) {
384 status.set(s_type, 1);
385 status.set(s_name, s_default_output_handler);
386 } else {
387 status.set(s_type, 0);
388 status.set(s_name, (*iter)->handler);
391 if (full) {
392 ret.append(status);
393 } else {
394 ret = std::move(status);
397 return ret;
400 void BaseExecutionContext::obSetImplicitFlush(bool on) {
401 m_implicitFlush = on;
404 Array BaseExecutionContext::obGetHandlers() {
405 Array ret;
406 for (std::list<OutputBuffer*>::const_iterator iter = m_buffers.begin();
407 iter != m_buffers.end(); ++iter) {
408 ret.append((*iter)->handler);
410 return ret;
413 void BaseExecutionContext::flush() {
414 if (m_buffers.empty()) {
415 fflush(stdout);
416 } else if (RuntimeOption::EnableEarlyFlush && m_protectedLevel &&
417 (m_transport == nullptr ||
418 (m_transport->getHTTPVersion() == "1.1" &&
419 m_transport->getMethod() != Transport::HEAD))) {
420 StringBuffer &oss = m_buffers.front()->oss;
421 if (!oss.empty()) {
422 if (m_transport) {
423 m_transport->sendRaw((void*)oss.data(), oss.size(), 200, false, true);
424 } else {
425 writeStdout(oss.data(), oss.size());
426 fflush(stdout);
428 oss.reset();
433 void BaseExecutionContext::resetCurrentBuffer() {
434 if (m_buffers.empty()) {
435 m_out = nullptr;
436 } else {
437 m_out = &m_buffers.back()->oss;
441 ///////////////////////////////////////////////////////////////////////////////
442 // program executions
444 void BaseExecutionContext::registerShutdownFunction(CVarRef function,
445 Array arguments,
446 ShutdownType type) {
447 Array callback = CREATE_MAP2(s_name, function, s_args, arguments);
448 Variant &funcs = m_shutdowns.lvalAt(type);
449 funcs.append(callback);
452 Variant BaseExecutionContext::pushUserErrorHandler(CVarRef function,
453 int error_types) {
454 Variant ret;
455 if (!m_userErrorHandlers.empty()) {
456 ret = m_userErrorHandlers.back().first;
458 m_userErrorHandlers.push_back(std::pair<Variant,int>(function, error_types));
459 return ret;
462 Variant BaseExecutionContext::pushUserExceptionHandler(CVarRef function) {
463 Variant ret;
464 if (!m_userExceptionHandlers.empty()) {
465 ret = m_userExceptionHandlers.back();
467 m_userExceptionHandlers.push_back(function);
468 return ret;
471 void BaseExecutionContext::popUserErrorHandler() {
472 if (!m_userErrorHandlers.empty()) {
473 m_userErrorHandlers.pop_back();
477 void BaseExecutionContext::popUserExceptionHandler() {
478 if (!m_userExceptionHandlers.empty()) {
479 m_userExceptionHandlers.pop_back();
483 void BaseExecutionContext::registerRequestEventHandler
484 (RequestEventHandler *handler) {
485 assert(handler);
486 if (m_requestEventHandlerSet.find(handler) ==
487 m_requestEventHandlerSet.end()) {
488 m_requestEventHandlerSet.insert(handler);
489 m_requestEventHandlers.push_back(handler);
490 } else {
491 assert(false);
495 static bool requestEventHandlerPriorityComp(RequestEventHandler *a,
496 RequestEventHandler *b) {
497 return a->priority() < b->priority();
500 void BaseExecutionContext::onRequestShutdown() {
501 // Sort handlers by priority so that lower priority values get shutdown
502 // first
503 sort(m_requestEventHandlers.begin(), m_requestEventHandlers.end(),
504 requestEventHandlerPriorityComp);
505 for (unsigned int i = 0; i < m_requestEventHandlers.size(); i++) {
506 RequestEventHandler *handler = m_requestEventHandlers[i];
507 assert(handler->getInited());
508 if (handler->getInited()) {
509 handler->requestShutdown();
510 handler->setInited(false);
513 m_requestEventHandlers.clear();
514 m_requestEventHandlerSet.clear();
517 void BaseExecutionContext::executeFunctions(CArrRef funcs) {
518 for (ArrayIter iter(funcs); iter; ++iter) {
519 Array callback = iter.second();
520 vm_call_user_func(callback[s_name], callback[s_args]);
524 void BaseExecutionContext::onShutdownPreSend() {
525 if (!m_shutdowns.isNull() && m_shutdowns.exists(ShutDown)) {
526 executeFunctions(m_shutdowns[ShutDown]);
527 m_shutdowns.remove(ShutDown);
529 obFlushAll(); // in case obStart was called without obFlush
532 void BaseExecutionContext::onShutdownPostSend() {
533 ServerStats::SetThreadMode(ServerStats::PostProcessing);
534 try {
535 try {
536 ServerStatsHelper ssh("psp", ServerStatsHelper::TRACK_HWINST);
537 if (!m_shutdowns.isNull()) {
538 if (m_shutdowns.exists(PostSend)) {
539 executeFunctions(m_shutdowns[PostSend]);
540 m_shutdowns.remove(PostSend);
542 if (m_shutdowns.exists(CleanUp)) {
543 executeFunctions(m_shutdowns[CleanUp]);
544 m_shutdowns.remove(CleanUp);
547 } catch (const ExitException &e) {
548 // do nothing
549 } catch (const Exception &e) {
550 onFatalError(e);
551 } catch (const Object &e) {
552 onUnhandledException(e);
554 } catch (...) {
555 Logger::Error("unknown exception was thrown from psp");
557 ServerStats::SetThreadMode(ServerStats::Idling);
560 ///////////////////////////////////////////////////////////////////////////////
561 // error handling
563 bool BaseExecutionContext::errorNeedsHandling(int errnum,
564 bool callUserHandler,
565 ErrorThrowMode mode) {
566 if (m_throwAllErrors) throw errnum;
567 if (mode != NeverThrow || (getErrorReportingLevel() & errnum) != 0 ||
568 RuntimeOption::NoSilencer) {
569 return true;
571 if (callUserHandler) {
572 if (!m_userErrorHandlers.empty() &&
573 (m_userErrorHandlers.back().second & errnum) != 0) {
574 return true;
577 return false;
580 class ErrorStateHelper {
581 public:
582 ErrorStateHelper(BaseExecutionContext *context, int state) {
583 m_context = context;
584 m_originalState = m_context->getErrorState();
585 m_context->setErrorState(state);
587 ~ErrorStateHelper() {
588 m_context->setErrorState(m_originalState);
590 private:
591 BaseExecutionContext *m_context;
592 int m_originalState;
595 static StaticString s_file("file");
596 static StaticString s_line("line");
598 void BaseExecutionContext::handleError(const std::string &msg,
599 int errnum,
600 bool callUserHandler,
601 ErrorThrowMode mode,
602 const std::string &prefix,
603 bool skipFrame /* = false */) {
604 SYNC_VM_REGS_SCOPED();
606 int newErrorState = ErrorRaised;
607 switch (getErrorState()) {
608 case ErrorRaised:
609 case ErrorRaisedByUserHandler:
610 return;
611 case ExecutingUserHandler:
612 newErrorState = ErrorRaisedByUserHandler;
613 break;
614 default:
615 break;
617 ErrorStateHelper esh(this, newErrorState);
618 ExtendedException ee = skipFrame ?
619 ExtendedException(ExtendedException::skipFrame, msg) :
620 ExtendedException(msg);
621 Array bt = ee.getBackTrace();
623 recordLastError(ee, errnum);
624 bool handled = false;
625 if (callUserHandler) {
626 handled = callUserErrorHandler(ee, errnum, false);
628 if (mode == AlwaysThrow || (mode == ThrowIfUnhandled && !handled)) {
629 try {
630 if (!Eval::Debugger::InterruptException(String(msg))) return;
631 } catch (const Eval::DebuggerClientExitException &e) {}
632 throw FatalErrorException(msg, bt);
634 if (!handled &&
635 (RuntimeOption::NoSilencer ||
636 (getErrorReportingLevel() & errnum) != 0)) {
637 try {
638 if (!Eval::Debugger::InterruptException(String(msg))) return;
639 } catch (const Eval::DebuggerClientExitException &e) {}
641 String file = empty_string;
642 int line = 0;
643 if (RuntimeOption::InjectedStackTrace) {
644 if (!bt.empty()) {
645 Array top = bt.rvalAt(0).toArray();
646 if (top.exists(s_file)) file = top.rvalAt(s_file).toString();
647 if (top.exists(s_line)) line = top.rvalAt(s_line);
651 Logger::Log(Logger::LogError, prefix.c_str(), ee, file.c_str(), line);
655 bool BaseExecutionContext::callUserErrorHandler(const Exception &e, int errnum,
656 bool swallowExceptions) {
657 switch (getErrorState()) {
658 case ExecutingUserHandler:
659 case ErrorRaisedByUserHandler:
660 return false;
661 default:
662 break;
664 if (!m_userErrorHandlers.empty() &&
665 (m_userErrorHandlers.back().second & errnum) != 0) {
666 int errline = 0;
667 String errfile;
668 Array backtrace;
669 const ExtendedException *ee = dynamic_cast<const ExtendedException*>(&e);
670 if (ee) {
671 Array arr = ee->getBackTrace();
672 if (!arr.isNull()) {
673 backtrace = arr;
674 Array top = backtrace.rvalAt(0);
675 if (!top.isNull()) {
676 errfile = top.rvalAt(s_file);
677 errline = top.rvalAt(s_line).toInt64();
681 try {
682 ErrorStateHelper esh(this, ExecutingUserHandler);
683 if (!same(vm_call_user_func
684 (m_userErrorHandlers.back().first,
685 CREATE_VECTOR6(errnum, String(e.getMessage()), errfile,
686 errline, "", backtrace)),
687 false)) {
688 return true;
690 } catch (...) {
691 if (!swallowExceptions) throw;
694 return false;
697 void BaseExecutionContext::recordLastError(const Exception &e,
698 int errnum /* = 0 */) {
699 m_lastError = String(e.getMessage());
700 m_lastErrorNum = errnum;
703 bool BaseExecutionContext::onFatalError(const Exception &e) {
704 recordLastError(e);
705 String file = empty_string;
706 int line = 0;
707 if (RuntimeOption::InjectedStackTrace) {
708 const ExtendedException *ee = dynamic_cast<const ExtendedException *>(&e);
709 if (ee) {
710 Array bt = ee->getBackTrace();
711 if (!bt.empty()) {
712 Array top = bt.rvalAt(0).toArray();
713 if (top.exists(s_file)) file = top.rvalAt(s_file).toString();
714 if (top.exists(s_line)) line = top.rvalAt(s_line);
718 if (RuntimeOption::AlwaysLogUnhandledExceptions) {
719 Logger::Log(Logger::LogError, "HipHop Fatal error: ", e,
720 file.c_str(), line);
722 bool handled = false;
723 if (RuntimeOption::CallUserHandlerOnFatals) {
724 int errnum = ErrorConstants::FATAL_ERROR;
725 handled = callUserErrorHandler(e, errnum, true);
727 if (!handled && !RuntimeOption::AlwaysLogUnhandledExceptions) {
728 Logger::Log(Logger::LogError, "HipHop Fatal error: ", e,
729 file.c_str(), line);
731 return handled;
734 bool BaseExecutionContext::onUnhandledException(Object e) {
735 String err = e.toString();
736 if (RuntimeOption::AlwaysLogUnhandledExceptions) {
737 Logger::Error("HipHop Fatal error: Uncaught %s", err.data());
740 if (e.instanceof(SystemLib::s_ExceptionClass)) {
741 // user thrown exception
742 if (!m_userExceptionHandlers.empty()) {
743 if (!same(vm_call_user_func
744 (m_userExceptionHandlers.back(),
745 CREATE_VECTOR1(e)),
746 false)) {
747 return true;
750 } else {
751 assert(false);
753 m_lastError = err;
755 if (!RuntimeOption::AlwaysLogUnhandledExceptions) {
756 Logger::Error("HipHop Fatal error: Uncaught %s", err.data());
758 return false;
761 void BaseExecutionContext::setLogErrors(bool on) {
762 if (m_logErrors != on) {
763 m_logErrors = on;
764 if (m_logErrors) {
765 if (!m_errorLog.empty()) {
766 FILE *output = fopen(m_errorLog.data(), "a");
767 if (output) {
768 Logger::SetNewOutput(output);
771 } else {
772 Logger::SetNewOutput(nullptr);
777 void BaseExecutionContext::setErrorLog(CStrRef filename) {
778 m_errorLog = filename;
779 if (m_logErrors && !m_errorLog.empty()) {
780 FILE *output = fopen(m_errorLog.data(), "a");
781 if (output) {
782 Logger::SetNewOutput(output);
787 ///////////////////////////////////////////////////////////////////////////////
788 // IDebuggable
790 void BaseExecutionContext::debuggerInfo(InfoVec &info) {
791 if (m_maxMemory <= 0) {
792 Add(info, "Max Memory", "(unlimited)");
793 } else {
794 Add(info, "Max Memory", FormatSize(m_maxMemory));
796 Add(info, "Max Time", FormatTime(m_maxTime * 1000));
799 ///////////////////////////////////////////////////////////////////////////////
801 void BaseExecutionContext::setenv(CStrRef name, CStrRef value) {
802 m_envs.set(name, value);
805 String BaseExecutionContext::getenv(CStrRef name) const {
806 if (m_envs.exists(name)) {
807 return m_envs[name];
809 char *value = ::getenv(name.data());
810 if (value) {
811 return String(value, CopyString);
813 if (RuntimeOption::EnvVariables.find(name.c_str()) != RuntimeOption::EnvVariables.end()) {
814 return String(RuntimeOption::EnvVariables[name.c_str()].data(), CopyString);
816 return String();
819 ///////////////////////////////////////////////////////////////////////////////
821 void BaseExecutionContext::setIncludePath(CStrRef path) {
822 m_include_paths = f_explode(":", path);
825 String BaseExecutionContext::getIncludePath() const {
826 StringBuffer sb;
827 bool first = true;
828 for (ArrayIter iter(m_include_paths); iter; ++iter) {
829 if (first) {
830 first = false;
831 } else {
832 sb.append(':');
834 sb.append(iter.second().toString());
836 return sb.detach();
839 ///////////////////////////////////////////////////////////////////////////////
840 // persistent objects
842 IMPLEMENT_THREAD_LOCAL_NO_CHECK(PersistentObjectStore, g_persistentObjects);
844 void PersistentObjectStore::removeObject(ResourceData *data) {
845 if (data) {
846 if (data->decRefCount() == 0) {
847 data->release();
848 } else {
849 SweepableResourceData *sw = dynamic_cast<SweepableResourceData*>(data);
850 if (sw) {
851 sw->decPersistent();
857 PersistentObjectStore::~PersistentObjectStore() {
858 for (ResourceMapMap::const_iterator iter = m_objects.begin();
859 iter != m_objects.end(); ++iter) {
860 const ResourceMap &resources = iter->second;
861 for (ResourceMap::const_iterator iterInner = resources.begin();
862 iterInner != resources.end(); ++iterInner) {
863 removeObject(iterInner->second);
868 int PersistentObjectStore::size() const {
869 int total = 0;
870 for (ResourceMapMap::const_iterator iter = m_objects.begin();
871 iter != m_objects.end(); ++iter) {
872 total += iter->second.size();
874 return total;
877 void PersistentObjectStore::set(const char *type, const char *name,
878 ResourceData *obj) {
879 assert(type && *type);
880 assert(name);
882 ResourceMap &resources = m_objects[type];
883 ResourceMap::iterator iter = resources.find(name);
884 if (iter != resources.end()) {
885 if (iter->second == obj) {
886 return; // we are setting the same object
888 removeObject(iter->second);
889 resources.erase(iter);
892 if (obj) {
893 obj->incRefCount();
894 SweepableResourceData *sw = dynamic_cast<SweepableResourceData*>(obj);
895 if (sw) {
896 sw->incPersistent();
898 m_objects[type][name] = obj;
902 ResourceData *PersistentObjectStore::get(const char *type, const char *name) {
903 assert(type && *type);
904 assert(name);
905 ResourceMap &resources = m_objects[type];
906 ResourceMap::const_iterator iter = resources.find(name);
907 if (iter == resources.end()) {
908 return nullptr;
910 return iter->second;
913 void PersistentObjectStore::remove(const char *type, const char *name) {
914 assert(type && *type);
915 assert(name);
916 ResourceMap &resources = m_objects[type];
917 ResourceMap::iterator iter = resources.find(name);
918 if (iter != resources.end()) {
919 removeObject(iter->second);
920 resources.erase(iter);
924 const ResourceMap &PersistentObjectStore::getMap(const char *type) {
925 assert(type && *type);
926 return m_objects[type];
929 ///////////////////////////////////////////////////////////////////////////////
930 // silencer
933 Silencer::Silencer(bool e) : m_active(false) {
934 if (e) enable();
937 void Silencer::enable() {
938 m_errorReportingValue = g_context->getErrorReportingLevel();
939 g_context->setErrorReportingLevel(0);
940 m_active = true;
943 void Silencer::disableHelper() {
944 if (m_active) {
945 if (g_context->getErrorReportingLevel() == 0)
946 g_context->setErrorReportingLevel(m_errorReportingValue);
950 Variant Silencer::disable(CVarRef v) {
951 disable();
952 return v;
955 ///////////////////////////////////////////////////////////////////////////////