2 +----------------------------------------------------------------------+
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
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>
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),
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),
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() {
109 for (std::list
<OutputBuffer
*>::const_iterator iter
= m_buffers
.begin();
110 iter
!= m_buffers
.end(); ++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
) {
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
;
128 StringData
* sd
= current
->first
;
129 Eval::PhpFile
* efile
= current
->second
;
130 efile
->decRefAndDelete();
131 m_evaledFiles
.erase(current
);
134 // Discard all units that were created via create_function().
135 for (EvaledUnitsVec::iterator it
= m_createdFuncs
.begin();
136 it
!= m_createdFuncs
.end(); ++it
) {
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 ///////////////////////////////////////////////////////////////////////////////
169 String
BaseExecutionContext::getMimeType() const {
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();
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
);
195 void BaseExecutionContext::setContentType(CStrRef mimetype
, CStrRef charset
) {
197 String contentType
= mimetype
;
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
) {
211 MemoryManager::TheMemoryManager()->getStats().maxBytes
= m_maxMemory
;
214 ///////////////////////////////////////////////////////////////////////////////
217 void BaseExecutionContext::write(CStrRef s
) {
218 write(s
.data(), s
.size());
221 void BaseExecutionContext::setStdout(PFUNC_STDOUT func
, void *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
));
235 safe_stdout(ANSI_COLOR_END
, strlen(ANSI_COLOR_END
));
240 m_stdout(s
, len
, m_stdoutData
);
244 void BaseExecutionContext::write(const char *s
, int len
) {
246 m_out
->append(s
, len
);
250 if (m_implicitFlush
) flush();
253 ///////////////////////////////////////////////////////////////////////////////
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
;
277 String
BaseExecutionContext::obDetachContents() {
278 if (!m_buffers
.empty()) {
279 StringBuffer
&oss
= m_buffers
.back()->oss
;
287 int BaseExecutionContext::obGetContentLength() {
288 if (m_buffers
.empty()) {
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
);
313 vm_call_user_func(last
->handler
,
314 CREATE_VECTOR2(last
->oss
.detach(), flag
));
315 prev
->oss
.append(tout
.toString());
318 prev
->oss
.absorb(last
->oss
);
324 if (!last
->handler
.isNull()) {
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());
336 writeStdout(last
->oss
.data(), last
->oss
.size());
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();
356 if (m_implicitFlush
) flush();
360 void BaseExecutionContext::obEndAll() {
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
380 for (; iter
!= m_buffers
.end(); ++iter
, ++level
) {
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
);
387 status
.set(s_type
, 0);
388 status
.set(s_name
, (*iter
)->handler
);
394 ret
= std::move(status
);
400 void BaseExecutionContext::obSetImplicitFlush(bool on
) {
401 m_implicitFlush
= on
;
404 Array
BaseExecutionContext::obGetHandlers() {
406 for (std::list
<OutputBuffer
*>::const_iterator iter
= m_buffers
.begin();
407 iter
!= m_buffers
.end(); ++iter
) {
408 ret
.append((*iter
)->handler
);
413 void BaseExecutionContext::flush() {
414 if (m_buffers
.empty()) {
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
;
423 m_transport
->sendRaw((void*)oss
.data(), oss
.size(), 200, false, true);
425 writeStdout(oss
.data(), oss
.size());
433 void BaseExecutionContext::resetCurrentBuffer() {
434 if (m_buffers
.empty()) {
437 m_out
= &m_buffers
.back()->oss
;
441 ///////////////////////////////////////////////////////////////////////////////
442 // program executions
444 void BaseExecutionContext::registerShutdownFunction(CVarRef function
,
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
,
455 if (!m_userErrorHandlers
.empty()) {
456 ret
= m_userErrorHandlers
.back().first
;
458 m_userErrorHandlers
.push_back(std::pair
<Variant
,int>(function
, error_types
));
462 Variant
BaseExecutionContext::pushUserExceptionHandler(CVarRef function
) {
464 if (!m_userExceptionHandlers
.empty()) {
465 ret
= m_userExceptionHandlers
.back();
467 m_userExceptionHandlers
.push_back(function
);
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
) {
486 if (m_requestEventHandlerSet
.find(handler
) ==
487 m_requestEventHandlerSet
.end()) {
488 m_requestEventHandlerSet
.insert(handler
);
489 m_requestEventHandlers
.push_back(handler
);
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
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
);
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
) {
549 } catch (const Exception
&e
) {
551 } catch (const Object
&e
) {
552 onUnhandledException(e
);
555 Logger::Error("unknown exception was thrown from psp");
557 ServerStats::SetThreadMode(ServerStats::Idling
);
560 ///////////////////////////////////////////////////////////////////////////////
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
) {
571 if (callUserHandler
) {
572 if (!m_userErrorHandlers
.empty() &&
573 (m_userErrorHandlers
.back().second
& errnum
) != 0) {
580 class ErrorStateHelper
{
582 ErrorStateHelper(BaseExecutionContext
*context
, int state
) {
584 m_originalState
= m_context
->getErrorState();
585 m_context
->setErrorState(state
);
587 ~ErrorStateHelper() {
588 m_context
->setErrorState(m_originalState
);
591 BaseExecutionContext
*m_context
;
595 static StaticString
s_file("file");
596 static StaticString
s_line("line");
598 void BaseExecutionContext::handleError(const std::string
&msg
,
600 bool callUserHandler
,
602 const std::string
&prefix
,
603 bool skipFrame
/* = false */) {
604 SYNC_VM_REGS_SCOPED();
606 int newErrorState
= ErrorRaised
;
607 switch (getErrorState()) {
609 case ErrorRaisedByUserHandler
:
611 case ExecutingUserHandler
:
612 newErrorState
= ErrorRaisedByUserHandler
;
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
)) {
630 if (!Eval::Debugger::InterruptException(String(msg
))) return;
631 } catch (const Eval::DebuggerClientExitException
&e
) {}
632 throw FatalErrorException(msg
, bt
);
635 (RuntimeOption::NoSilencer
||
636 (getErrorReportingLevel() & errnum
) != 0)) {
638 if (!Eval::Debugger::InterruptException(String(msg
))) return;
639 } catch (const Eval::DebuggerClientExitException
&e
) {}
641 String file
= empty_string
;
643 if (RuntimeOption::InjectedStackTrace
) {
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
:
664 if (!m_userErrorHandlers
.empty() &&
665 (m_userErrorHandlers
.back().second
& errnum
) != 0) {
669 const ExtendedException
*ee
= dynamic_cast<const ExtendedException
*>(&e
);
671 Array arr
= ee
->getBackTrace();
674 Array top
= backtrace
.rvalAt(0);
676 errfile
= top
.rvalAt(s_file
);
677 errline
= top
.rvalAt(s_line
).toInt64();
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
)),
691 if (!swallowExceptions
) throw;
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
) {
705 String file
= empty_string
;
707 if (RuntimeOption::InjectedStackTrace
) {
708 const ExtendedException
*ee
= dynamic_cast<const ExtendedException
*>(&e
);
710 Array bt
= ee
->getBackTrace();
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
,
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
,
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(),
755 if (!RuntimeOption::AlwaysLogUnhandledExceptions
) {
756 Logger::Error("HipHop Fatal error: Uncaught %s", err
.data());
761 void BaseExecutionContext::setLogErrors(bool on
) {
762 if (m_logErrors
!= on
) {
765 if (!m_errorLog
.empty()) {
766 FILE *output
= fopen(m_errorLog
.data(), "a");
768 Logger::SetNewOutput(output
);
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");
782 Logger::SetNewOutput(output
);
787 ///////////////////////////////////////////////////////////////////////////////
790 void BaseExecutionContext::debuggerInfo(InfoVec
&info
) {
791 if (m_maxMemory
<= 0) {
792 Add(info
, "Max Memory", "(unlimited)");
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
)) {
809 char *value
= ::getenv(name
.data());
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
);
819 ///////////////////////////////////////////////////////////////////////////////
821 void BaseExecutionContext::setIncludePath(CStrRef path
) {
822 m_include_paths
= f_explode(":", path
);
825 String
BaseExecutionContext::getIncludePath() const {
828 for (ArrayIter
iter(m_include_paths
); iter
; ++iter
) {
834 sb
.append(iter
.second().toString());
839 ///////////////////////////////////////////////////////////////////////////////
840 // persistent objects
842 IMPLEMENT_THREAD_LOCAL_NO_CHECK(PersistentObjectStore
, g_persistentObjects
);
844 void PersistentObjectStore::removeObject(ResourceData
*data
) {
846 if (data
->decRefCount() == 0) {
849 SweepableResourceData
*sw
= dynamic_cast<SweepableResourceData
*>(data
);
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 {
870 for (ResourceMapMap::const_iterator iter
= m_objects
.begin();
871 iter
!= m_objects
.end(); ++iter
) {
872 total
+= iter
->second
.size();
877 void PersistentObjectStore::set(const char *type
, const char *name
,
879 assert(type
&& *type
);
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
);
894 SweepableResourceData
*sw
= dynamic_cast<SweepableResourceData
*>(obj
);
898 m_objects
[type
][name
] = obj
;
902 ResourceData
*PersistentObjectStore::get(const char *type
, const char *name
) {
903 assert(type
&& *type
);
905 ResourceMap
&resources
= m_objects
[type
];
906 ResourceMap::const_iterator iter
= resources
.find(name
);
907 if (iter
== resources
.end()) {
913 void PersistentObjectStore::remove(const char *type
, const char *name
) {
914 assert(type
&& *type
);
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 ///////////////////////////////////////////////////////////////////////////////
933 Silencer::Silencer(bool e
) : m_active(false) {
937 void Silencer::enable() {
938 m_errorReportingValue
= g_context
->getErrorReportingLevel();
939 g_context
->setErrorReportingLevel(0);
943 void Silencer::disableHelper() {
945 if (g_context
->getErrorReportingLevel() == 0)
946 g_context
->setErrorReportingLevel(m_errorReportingValue
);
950 Variant
Silencer::disable(CVarRef v
) {
955 ///////////////////////////////////////////////////////////////////////////////