Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/scriptinterface/ScriptRuntime.cpp
Show All 16 Lines | |||||
#include "precompiled.h" | #include "precompiled.h" | ||||
#include "ScriptRuntime.h" | #include "ScriptRuntime.h" | ||||
#include "ps/GameSetup/Config.h" | #include "ps/GameSetup/Config.h" | ||||
#include "ps/Profile.h" | #include "ps/Profile.h" | ||||
#include "scriptinterface/ScriptEngine.h" | #include "scriptinterface/ScriptEngine.h" | ||||
#include "scriptinterface/ScriptInterface.h" | |||||
void GCSliceCallbackHook(JSRuntime* UNUSED(rt), JS::GCProgress progress, const JS::GCDescription& UNUSED(desc)) | void GCSliceCallbackHook(JSRuntime* UNUSED(rt), JS::GCProgress progress, const JS::GCDescription& UNUSED(desc)) | ||||
{ | { | ||||
/* | /* | ||||
* During non-incremental GC, the GC is bracketed by JSGC_CYCLE_BEGIN/END | * During non-incremental GC, the GC is bracketed by JSGC_CYCLE_BEGIN/END | ||||
* callbacks. During an incremental GC, the sequence of callbacks is as | * callbacks. During an incremental GC, the sequence of callbacks is as | ||||
* follows: | * follows: | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | void GCSliceCallbackHook(JSRuntime* UNUSED(rt), JS::GCProgress progress, const JS::GCDescription& UNUSED(desc)) | ||||
{ | { | ||||
outstring[i] = (wchar_t)str[i]; | outstring[i] = (wchar_t)str[i]; | ||||
} | } | ||||
printf("---------------------------------------\n: %ls \n---------------------------------------\n", outstring); | printf("---------------------------------------\n: %ls \n---------------------------------------\n", outstring); | ||||
#endif | #endif | ||||
} | } | ||||
namespace { | |||||
void ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report) | |||||
{ | |||||
ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); | |||||
std::stringstream msg; | |||||
bool isWarning = JSREPORT_IS_WARNING(report->flags); | |||||
msg << (isWarning ? "JavaScript warning: " : "JavaScript error: "); | |||||
if (report->filename) | |||||
{ | |||||
msg << report->filename; | |||||
msg << " line " << report->lineno << "\n"; | |||||
} | |||||
msg << message; | |||||
// If there is an exception, then print its stack trace | |||||
JS::RootedValue excn(rq.cx); | |||||
if (JS_GetPendingException(rq.cx, &excn) && excn.isObject()) | |||||
{ | |||||
JS::RootedValue stackVal(rq.cx); | |||||
JS::RootedObject excnObj(rq.cx, &excn.toObject()); | |||||
JS_GetProperty(rq.cx, excnObj, "stack", &stackVal); | |||||
std::string stackText; | |||||
ScriptInterface::FromJSVal(rq, stackVal, stackText); | |||||
std::istringstream stream(stackText); | |||||
for (std::string line; std::getline(stream, line);) | |||||
msg << "\n " << line; | |||||
} | |||||
if (isWarning) | |||||
LOGWARNING("%s", msg.str().c_str()); | |||||
else | |||||
LOGERROR("%s", msg.str().c_str()); | |||||
// When running under Valgrind, print more information in the error message | |||||
// VALGRIND_PRINTF_BACKTRACE("->"); | |||||
} | |||||
} // anonymous namespace | |||||
shared_ptr<ScriptRuntime> ScriptRuntime::CreateRuntime(int runtimeSize, int heapGrowthBytesGCTrigger) | shared_ptr<ScriptRuntime> ScriptRuntime::CreateRuntime(int runtimeSize, int heapGrowthBytesGCTrigger) | ||||
{ | { | ||||
return shared_ptr<ScriptRuntime>(new ScriptRuntime(runtimeSize, heapGrowthBytesGCTrigger)); | return shared_ptr<ScriptRuntime>(new ScriptRuntime(runtimeSize, heapGrowthBytesGCTrigger)); | ||||
} | } | ||||
ScriptRuntime::ScriptRuntime(int runtimeSize, int heapGrowthBytesGCTrigger): | ScriptRuntime::ScriptRuntime(int runtimeSize, int heapGrowthBytesGCTrigger): | ||||
m_LastGCBytes(0), | m_LastGCBytes(0), | ||||
m_LastGCCheck(0.0f), | m_LastGCCheck(0.0f), | ||||
Show All 11 Lines | ScriptRuntime::ScriptRuntime(int runtimeSize, int heapGrowthBytesGCTrigger): | ||||
JS_SetGCParameter(m_rt, JSGC_MAX_BYTES, m_RuntimeSize); | JS_SetGCParameter(m_rt, JSGC_MAX_BYTES, m_RuntimeSize); | ||||
JS_SetGCParameter(m_rt, JSGC_MODE, JSGC_MODE_INCREMENTAL); | JS_SetGCParameter(m_rt, JSGC_MODE, JSGC_MODE_INCREMENTAL); | ||||
// The whole heap-growth mechanism seems to work only for non-incremental GCs. | // The whole heap-growth mechanism seems to work only for non-incremental GCs. | ||||
// We disable it to make it more clear if full GCs happen triggered by this JSAPI internal mechanism. | // We disable it to make it more clear if full GCs happen triggered by this JSAPI internal mechanism. | ||||
JS_SetGCParameter(m_rt, JSGC_DYNAMIC_HEAP_GROWTH, false); | JS_SetGCParameter(m_rt, JSGC_DYNAMIC_HEAP_GROWTH, false); | ||||
ScriptEngine::GetSingleton().RegisterRuntime(m_rt); | ScriptEngine::GetSingleton().RegisterRuntime(m_rt); | ||||
m_cx = JS_NewContext(m_rt, STACK_CHUNK_SIZE); | |||||
ENSURE(m_cx); // TODO: error handling | |||||
JS_SetOffthreadIonCompilationEnabled(m_rt, true); | |||||
// For GC debugging: | |||||
// JS_SetGCZeal(m_cx, 2, JS_DEFAULT_ZEAL_FREQ); | |||||
JS_SetContextPrivate(m_cx, nullptr); | |||||
JS_SetErrorReporter(m_rt, ErrorReporter); | |||||
JS_SetGlobalJitCompilerOption(m_rt, JSJITCOMPILER_ION_ENABLE, 1); | |||||
JS_SetGlobalJitCompilerOption(m_rt, JSJITCOMPILER_BASELINE_ENABLE, 1); | |||||
JS::RuntimeOptionsRef(m_cx) | |||||
.setExtraWarnings(true) | |||||
.setWerror(false) | |||||
.setStrictMode(true); | |||||
} | } | ||||
ScriptRuntime::~ScriptRuntime() | ScriptRuntime::~ScriptRuntime() | ||||
{ | { | ||||
JS_DestroyContext(m_cx); | |||||
JS_DestroyRuntime(m_rt); | JS_DestroyRuntime(m_rt); | ||||
ENSURE(ScriptEngine::IsInitialised() && "The ScriptEngine must be active (initialized and not yet shut down) when destroying a ScriptRuntime!"); | ENSURE(ScriptEngine::IsInitialised() && "The ScriptEngine must be active (initialized and not yet shut down) when destroying a ScriptRuntime!"); | ||||
ScriptEngine::GetSingleton().UnRegisterRuntime(m_rt); | ScriptEngine::GetSingleton().UnRegisterRuntime(m_rt); | ||||
} | } | ||||
void ScriptRuntime::RegisterContext(JSContext* cx) | void ScriptRuntime::RegisterCompartment(JSCompartment* cmpt) | ||||
{ | { | ||||
m_Contexts.push_back(cx); | ENSURE(cmpt); | ||||
m_Compartments.push_back(cmpt); | |||||
} | } | ||||
void ScriptRuntime::UnRegisterContext(JSContext* cx) | void ScriptRuntime::UnRegisterCompartment(JSCompartment* cmpt) | ||||
{ | { | ||||
m_Contexts.remove(cx); | m_Compartments.remove(cmpt); | ||||
} | } | ||||
#define GC_DEBUG_PRINT 0 | #define GC_DEBUG_PRINT 0 | ||||
void ScriptRuntime::MaybeIncrementalGC(double delay) | void ScriptRuntime::MaybeIncrementalGC(double delay) | ||||
{ | { | ||||
PROFILE2("MaybeIncrementalGC"); | PROFILE2("MaybeIncrementalGC"); | ||||
if (JS::IsIncrementalGCEnabled(m_rt)) | if (JS::IsIncrementalGCEnabled(m_rt)) | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | #endif | ||||
// fast enough. | // fast enough. | ||||
if (gcBytes > m_RuntimeSize / 2) | if (gcBytes > m_RuntimeSize / 2) | ||||
{ | { | ||||
if (JS::IsIncrementalGCInProgress(m_rt)) | if (JS::IsIncrementalGCInProgress(m_rt)) | ||||
{ | { | ||||
#if GC_DEBUG_PRINT | #if GC_DEBUG_PRINT | ||||
printf("Finishing incremental GC because gcBytes > m_RuntimeSize / 2. \n"); | printf("Finishing incremental GC because gcBytes > m_RuntimeSize / 2. \n"); | ||||
#endif | #endif | ||||
PrepareContextsForIncrementalGC(); | PrepareCompartmentsForIncrementalGC(); | ||||
JS::FinishIncrementalGC(m_rt, JS::gcreason::REFRESH_FRAME); | JS::FinishIncrementalGC(m_rt, JS::gcreason::REFRESH_FRAME); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
if (gcBytes > m_RuntimeSize * 0.75) | if (gcBytes > m_RuntimeSize * 0.75) | ||||
{ | { | ||||
ShrinkingGC(); | ShrinkingGC(); | ||||
#if GC_DEBUG_PRINT | #if GC_DEBUG_PRINT | ||||
Show All 12 Lines | #endif | ||||
else | else | ||||
{ | { | ||||
#if GC_DEBUG_PRINT | #if GC_DEBUG_PRINT | ||||
if (!JS::IsIncrementalGCInProgress(m_rt)) | if (!JS::IsIncrementalGCInProgress(m_rt)) | ||||
printf("Starting incremental GC \n"); | printf("Starting incremental GC \n"); | ||||
else | else | ||||
printf("Running incremental GC slice \n"); | printf("Running incremental GC slice \n"); | ||||
#endif | #endif | ||||
PrepareContextsForIncrementalGC(); | PrepareCompartmentsForIncrementalGC(); | ||||
if (!JS::IsIncrementalGCInProgress(m_rt)) | if (!JS::IsIncrementalGCInProgress(m_rt)) | ||||
JS::StartIncrementalGC(m_rt, GC_NORMAL, JS::gcreason::REFRESH_FRAME, GCSliceTimeBudget); | JS::StartIncrementalGC(m_rt, GC_NORMAL, JS::gcreason::REFRESH_FRAME, GCSliceTimeBudget); | ||||
else | else | ||||
JS::IncrementalGCSlice(m_rt, JS::gcreason::REFRESH_FRAME, GCSliceTimeBudget); | JS::IncrementalGCSlice(m_rt, JS::gcreason::REFRESH_FRAME, GCSliceTimeBudget); | ||||
} | } | ||||
m_LastGCBytes = gcBytes; | m_LastGCBytes = gcBytes; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void ScriptRuntime::ShrinkingGC() | void ScriptRuntime::ShrinkingGC() | ||||
{ | { | ||||
JS_SetGCParameter(m_rt, JSGC_MODE, JSGC_MODE_COMPARTMENT); | JS_SetGCParameter(m_rt, JSGC_MODE, JSGC_MODE_COMPARTMENT); | ||||
JS::PrepareForFullGC(m_rt); | JS::PrepareForFullGC(m_rt); | ||||
JS::GCForReason(m_rt, GC_SHRINK, JS::gcreason::REFRESH_FRAME); | JS::GCForReason(m_rt, GC_SHRINK, JS::gcreason::REFRESH_FRAME); | ||||
JS_SetGCParameter(m_rt, JSGC_MODE, JSGC_MODE_INCREMENTAL); | JS_SetGCParameter(m_rt, JSGC_MODE, JSGC_MODE_INCREMENTAL); | ||||
} | } | ||||
void ScriptRuntime::PrepareContextsForIncrementalGC() | void ScriptRuntime::PrepareCompartmentsForIncrementalGC() const | ||||
{ | { | ||||
for (JSContext* const& ctx : m_Contexts) | for (JSCompartment* const& cmpt : m_Compartments) | ||||
JS::PrepareZoneForGC(js::GetCompartmentZone(js::GetContextCompartment(ctx))); | JS::PrepareZoneForGC(js::GetCompartmentZone(cmpt)); | ||||
} | } |
Wildfire Games · Phabricator