Index: ps/trunk/source/scriptinterface/ScriptInterface.h =================================================================== --- ps/trunk/source/scriptinterface/ScriptInterface.h +++ ps/trunk/source/scriptinterface/ScriptInterface.h @@ -135,8 +135,8 @@ /** * Set the named property on the global object. - * If @p replace is true, an existing property will be overwritten; otherwise attempts - * to set an already-defined value will fail. + * Optionally makes it {ReadOnly, DontEnum}. We do not allow to make it DontDelete, so that it can be hotloaded + * by deleting it and re-creating it, which is done by setting @p replace to true. */ template bool SetGlobal(const char* name, const T& value, bool replace = false, bool constant = true, bool enumerate = true); @@ -360,9 +360,9 @@ bool Eval_(const char* code, JS::MutableHandleValue ret) const; bool Eval_(const wchar_t* code, JS::MutableHandleValue ret) const; bool SetGlobal_(const char* name, JS::HandleValue value, bool replace, bool constant, bool enumerate); - bool SetProperty_(JS::HandleValue obj, const char* name, JS::HandleValue value, bool readonly, bool enumerate) const; - bool SetProperty_(JS::HandleValue obj, const wchar_t* name, JS::HandleValue value, bool readonly, bool enumerate) const; - bool SetPropertyInt_(JS::HandleValue obj, int name, JS::HandleValue value, bool readonly, bool enumerate) const; + bool SetProperty_(JS::HandleValue obj, const char* name, JS::HandleValue value, bool constant, bool enumerate) const; + bool SetProperty_(JS::HandleValue obj, const wchar_t* name, JS::HandleValue value, bool constant, bool enumerate) const; + bool SetPropertyInt_(JS::HandleValue obj, int name, JS::HandleValue value, bool constant, bool enumerate) const; bool GetProperty_(JS::HandleValue obj, const char* name, JS::MutableHandleValue out) const; bool GetPropertyInt_(JS::HandleValue obj, int name, JS::MutableHandleValue value) const; static bool IsExceptionPending(JSContext* cx); @@ -494,30 +494,30 @@ } template -bool ScriptInterface::SetProperty(JS::HandleValue obj, const char* name, const T& value, bool readonly, bool enumerate) const +bool ScriptInterface::SetProperty(JS::HandleValue obj, const char* name, const T& value, bool constant, bool enumerate) const { JSAutoRequest rq(GetContext()); JS::RootedValue val(GetContext()); AssignOrToJSVal(GetContext(), &val, value); - return SetProperty_(obj, name, val, readonly, enumerate); + return SetProperty_(obj, name, val, constant, enumerate); } template -bool ScriptInterface::SetProperty(JS::HandleValue obj, const wchar_t* name, const T& value, bool readonly, bool enumerate) const +bool ScriptInterface::SetProperty(JS::HandleValue obj, const wchar_t* name, const T& value, bool constant, bool enumerate) const { JSAutoRequest rq(GetContext()); JS::RootedValue val(GetContext()); AssignOrToJSVal(GetContext(), &val, value); - return SetProperty_(obj, name, val, readonly, enumerate); + return SetProperty_(obj, name, val, constant, enumerate); } template -bool ScriptInterface::SetPropertyInt(JS::HandleValue obj, int name, const T& value, bool readonly, bool enumerate) const +bool ScriptInterface::SetPropertyInt(JS::HandleValue obj, int name, const T& value, bool constant, bool enumerate) const { JSAutoRequest rq(GetContext()); JS::RootedValue val(GetContext()); AssignOrToJSVal(GetContext(), &val, value); - return SetPropertyInt_(obj, name, val, readonly, enumerate); + return SetPropertyInt_(obj, name, val, constant, enumerate); } template Index: ps/trunk/source/scriptinterface/ScriptInterface.cpp =================================================================== --- ps/trunk/source/scriptinterface/ScriptInterface.cpp +++ ps/trunk/source/scriptinterface/ScriptInterface.cpp @@ -589,21 +589,40 @@ { JSAutoRequest rq(m->m_cx); JS::RootedObject global(m->m_cx, m->m_glob); - if (!replace) + + bool found; + if (!JS_HasProperty(m->m_cx, global, name, &found)) + return false; + if (found) { - bool found; - if (!JS_HasProperty(m->m_cx, global, name, &found)) + JS::Rooted desc(m->m_cx); + if (!JS_GetOwnPropertyDescriptor(m->m_cx, global, name, &desc)) return false; - if (found) + + if (desc.isReadonly()) { - JS_ReportError(m->m_cx, "SetGlobal \"%s\" called multiple times", name); - return false; + if (!replace) + { + JS_ReportError(m->m_cx, "SetGlobal \"%s\" called multiple times", name); + return false; + } + + // This is not supposed to happen, unless the user has called SetProperty with constant = true on the global object + // instead of using SetGlobal. + if (desc.isPermanent()) + { + JS_ReportError(m->m_cx, "The global \"%s\" is permanent and cannot be hotloaded", name); + return false; + } + + LOGMESSAGE("Hotloading new value for global \"%s\".", name); + ENSURE(JS_DeleteProperty(m->m_cx, global, name)); } } uint attrs = 0; if (constant) - attrs |= JSPROP_READONLY | JSPROP_PERMANENT; + attrs |= JSPROP_READONLY; if (enumerate) attrs |= JSPROP_ENUMERATE; Index: ps/trunk/source/simulation2/system/ComponentManager.cpp =================================================================== --- ps/trunk/source/simulation2/system/ComponentManager.cpp +++ ps/trunk/source/simulation2/system/ComponentManager.cpp @@ -145,6 +145,8 @@ return false; std::string content = file.DecodeUTF8(); // assume it's UTF-8 bool ok = m_ScriptInterface.LoadScript(filename, content); + + m_CurrentlyHotloading = false; return ok; } @@ -374,9 +376,6 @@ void CComponentManager::Script_RegisterGlobal(ScriptInterface::CxPrivate* pCxPrivate, const std::string& name, JS::HandleValue value) { CComponentManager* componentManager = static_cast (pCxPrivate->pCBData); - - // Set the value, and accept duplicates only if hotloading (otherwise it's an error, - // in order to detect accidental duplicate definitions of globals) componentManager->m_ScriptInterface.SetGlobal(name.c_str(), value, componentManager->m_CurrentlyHotloading); }