Index: libraries/source/spidermonkey/ExportMovableCellHasher.diff =================================================================== --- /dev/null +++ libraries/source/spidermonkey/ExportMovableCellHasher.diff @@ -0,0 +1,37 @@ +diff --git a/js/src/gc/Barrier.cpp b/js/src/gc/Barrier.cpp +--- a/js/src/gc/Barrier.cpp 2016-04-14 19:55:21.000000000 +0200 ++++ b/js/src/gc/Barrier.cpp 2020-07-16 21:13:29.516760100 +0200 +@@ -158,11 +158,14 @@ + return uidK == uidL; + } + +-template struct MovableCellHasher; +-template struct MovableCellHasher; +-template struct MovableCellHasher; +-template struct MovableCellHasher; +-template struct MovableCellHasher; ++#if !MOZ_IS_GCC ++template struct JS_PUBLIC_API(MovableCellHasher); ++#endif ++ ++template struct JS_PUBLIC_API(MovableCellHasher); ++template struct JS_PUBLIC_API(MovableCellHasher); ++template struct JS_PUBLIC_API(MovableCellHasher); ++template struct JS_PUBLIC_API(MovableCellHasher); + + } // namespace js + +diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h +--- a/js/src/gc/Barrier.h 2016-04-14 19:55:21.000000000 +0200 ++++ b/js/src/gc/Barrier.h 2020-07-16 21:13:43.147969500 +0200 +@@ -785,6 +785,10 @@ + const T* address() { return &value; } + }; + ++#if MOZ_IS_GCC ++template struct JS_PUBLIC_API(MovableCellHasher); ++#endif ++ + template + struct MovableCellHasher> + { Index: libraries/source/spidermonkey/patch.sh =================================================================== --- libraries/source/spidermonkey/patch.sh +++ libraries/source/spidermonkey/patch.sh @@ -49,6 +49,12 @@ # https://bugzilla.mozilla.org/show_bug.cgi?id=1316079 patch -p1 < ../ExportJSPropertyDescriptor.diff +# MovableCellHasher specializations are only marked as exported in SM55 +# (see: https://bugzilla.mozilla.org/show_bug.cgi?id=1346389) +# Additionally, it is necessary to work around a bug in GCC (fix included in SM68) +# (see: https://bugzilla.mozilla.org/show_bug.cgi?id=1560064) +patch -p1 < ../ExportMovableCellHasher.diff + # When trying to link pyrogenesis, js::oom::GetThreadType() and js::ReportOutOfMemory() # are marked as unresolved symbols. # Will be included in SM52. Index: source/scriptinterface/ScriptRuntime.h =================================================================== --- source/scriptinterface/ScriptRuntime.h +++ source/scriptinterface/ScriptRuntime.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -57,32 +57,18 @@ void RegisterContext(JSContext* cx); void UnRegisterContext(JSContext* cx); - /** - * Registers an object to be freed/finalized by the ScriptRuntime. Freeing is - * guaranteed to happen after the next minor GC has completed, but might also - * happen a bit later. This is only needed in very special situations - * and you should only use it if you know exactly why you need it! - * Specify a deleter for the shared_ptr to free the void pointer correctly - * (by casting to the right type before calling delete for example). - */ - void AddDeferredFinalizationObject(const std::shared_ptr& obj); - JSRuntime* m_rt; private: void PrepareContextsForIncrementalGC(); - void GCCallbackMember(); std::list m_Contexts; - std::vector > m_FinalizationListObjectIdCache; int m_RuntimeSize; int m_HeapGrowthBytesGCTrigger; int m_LastGCBytes; double m_LastGCCheck; - - static void GCCallback(JSRuntime *rt, JSGCStatus status, void *data); }; #endif // INCLUDED_SCRIPTRUNTIME Index: source/scriptinterface/ScriptRuntime.cpp =================================================================== --- source/scriptinterface/ScriptRuntime.cpp +++ source/scriptinterface/ScriptRuntime.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2016 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -89,22 +89,6 @@ #endif } -void ScriptRuntime::GCCallback(JSRuntime* UNUSED(rt), JSGCStatus status, void *data) -{ - if (status == JSGC_END) - reinterpret_cast(data)->GCCallbackMember(); -} - -void ScriptRuntime::GCCallbackMember() -{ - m_FinalizationListObjectIdCache.clear(); -} - -void ScriptRuntime::AddDeferredFinalizationObject(const std::shared_ptr& obj) -{ - m_FinalizationListObjectIdCache.push_back(obj); -} - ScriptRuntime::ScriptRuntime(shared_ptr parentRuntime, int runtimeSize, int heapGrowthBytesGCTrigger): m_LastGCBytes(0), m_LastGCCheck(0.0f), @@ -118,7 +102,6 @@ ENSURE(m_rt); // TODO: error handling JS::SetGCSliceCallback(m_rt, GCSliceCallbackHook); - JS_SetGCCallback(m_rt, ScriptRuntime::GCCallback, this); JS_SetGCParameter(m_rt, JSGC_MAX_MALLOC_BYTES, m_RuntimeSize); JS_SetGCParameter(m_rt, JSGC_MAX_BYTES, m_RuntimeSize); @@ -133,9 +116,7 @@ ScriptRuntime::~ScriptRuntime() { - JS_SetGCCallback(m_rt, nullptr, nullptr); JS_DestroyRuntime(m_rt); - ENSURE(m_FinalizationListObjectIdCache.empty() && "Leak: Removing callback while some objects still aren't finalized!"); ENSURE(ScriptEngine::IsInitialised() && "The ScriptEngine must be active (initialized and not yet shut down) when destroying a ScriptRuntime!"); ScriptEngine::GetSingleton().UnRegisterRuntime(m_rt); Index: source/scriptinterface/tests/test_ObjectToIDMap.h =================================================================== --- source/scriptinterface/tests/test_ObjectToIDMap.h +++ source/scriptinterface/tests/test_ObjectToIDMap.h @@ -34,7 +34,7 @@ ObjectIdCache map(g_ScriptRuntime); map.init(); - TS_ASSERT(map.add(cx, obj, 1)); + TS_ASSERT(map.add(obj, 1)); JSObject* plainObj = obj; // The map should contain the object we've just added TS_ASSERT(map.has(plainObj)); Index: source/scriptinterface/third_party/ObjectToIDMap.h =================================================================== --- source/scriptinterface/third_party/ObjectToIDMap.h +++ source/scriptinterface/third_party/ObjectToIDMap.h @@ -8,8 +8,8 @@ * with correct garbage collection handling (JSObjects can move in memory). * * The code in this class was copied from here and modified to work in our environment. - * * https://mxr.mozilla.org/mozilla-esr38/source/js/ipc/JavaScriptShared.h - * * https://mxr.mozilla.org/mozilla-esr38/source/js/ipc/JavaScriptShared.cpp + * * https://dxr.mozilla.org/mozilla-esr45/source/js/ipc/JavaScriptShared.h + * * https://dxr.mozilla.org/mozilla-esr45/source/js/ipc/JavaScriptShared.cpp * * When updating SpiderMonkey, you most likely have to reintegrate an updated version * of the class(es) in this file. The best way is probably to get a diff between the @@ -22,111 +22,97 @@ #include "scriptinterface/ScriptRuntime.h" #include "scriptinterface/ScriptTypes.h" + #include +#include "js/GCHashTable.h" + +template<> +struct js::DefaultGCPolicy : public js::IgnoreGCPolicy {}; // Map JSObjects -> ids template class ObjectIdCache { - typedef js::PointerHasher Hasher; - typedef js::HashMap Table; + using Hasher = js::MovableCellHasher>; + using Table = js::GCHashMap, T, Hasher, js::SystemAllocPolicy>; NONCOPYABLE(ObjectIdCache); public: - ObjectIdCache(shared_ptr rt) - : table_(nullptr), m_rt(rt) + explicit ObjectIdCache(shared_ptr rt) + : m_rt(rt) { } ~ObjectIdCache() { - if (table_) - { - m_rt->AddDeferredFinalizationObject(std::shared_ptr((void*)table_, DeleteTable)); - table_ = nullptr; + JS_ClearAllPostBarrierCallbacks(m_rt->m_rt); + if (table_.initialized()) JS_RemoveExtraGCRootsTracer(m_rt->m_rt, ObjectIdCache::Trace, this); - } } bool init() { - if (table_) + if (table_.initialized()) return true; - table_ = new Table(js::SystemAllocPolicy()); JS_AddExtraGCRootsTracer(m_rt->m_rt, ObjectIdCache::Trace, this); - return table_ && table_->init(32); + return table_.init(32); } void trace(JSTracer* trc) { - for (typename Table::Enum e(*table_); !e.empty(); e.popFront()) - { - JSObject* obj = e.front().key(); - JS_CallUnbarrieredObjectTracer(trc, &obj, "ipc-object"); - if (obj != e.front().key()) - e.rekeyFront(obj); - } + table_.trace(trc); } -// TODO sweep? + void sweep() + { + table_.sweep(); + } bool find(JSObject* obj, T& ret) { - typename Table::Ptr p = table_->lookup(obj); + Table::Ptr p = table_.lookup(obj); if (!p) return false; ret = p->value(); return true; } - bool add(JSContext* cx, JSObject* obj, T id) + bool add(JSObject* obj, T id) { - if (!table_->put(obj, id)) - return false; - JS_StoreObjectPostBarrierCallback(cx, keyMarkCallback, obj, table_); - return true; + return table_.put(obj, id); } void remove(JSObject* obj) { - table_->remove(obj); + table_.remove(obj); } -// TODO clear? + void clear() + { + table_.clear(); + } bool empty() { - return table_->empty(); + return table_.empty(); } bool has(JSObject* obj) { - return table_->has(obj); + return table_.has(obj); } private: - static void keyMarkCallback(JSTracer* trc, JSObject* key, void* data) - { - Table* table = static_cast(data); - JSObject* prior = key; - JS_CallUnbarrieredObjectTracer(trc, &key, "ObjectIdCache::table_ key"); - table->rekeyIfMoved(prior, key); - } static void Trace(JSTracer* trc, void* data) { reinterpret_cast(data)->trace(trc); } - static void DeleteTable(void* table) - { - delete (Table*)table; - } - shared_ptr m_rt; - Table* table_; + Table table_; }; #endif // INCLUDED_OBJECTTOIDMAP Index: source/simulation2/components/CCmpAIManager.cpp =================================================================== --- source/simulation2/components/CCmpAIManager.cpp +++ source/simulation2/components/CCmpAIManager.cpp @@ -841,7 +841,7 @@ LOGERROR("RegisterSerializablePrototype called with same prototype multiple times: p=%p n='%s'", (void *)obj.get(), utf8_from_wstring(name)); return; } - m_SerializablePrototypes->add(cx, obj, name); + m_SerializablePrototypes->add(obj, name); m_DeserializablePrototypes[name] = JS::Heap(obj); } Index: source/simulation2/serialization/BinarySerializer.cpp =================================================================== --- source/simulation2/serialization/BinarySerializer.cpp +++ source/simulation2/serialization/BinarySerializer.cpp @@ -468,7 +468,7 @@ JSContext* cx = m_ScriptInterface.GetContext(); JSAutoRequest rq(cx); - m_ScriptBackrefs.add(cx, obj, m_ScriptBackrefsNext); + m_ScriptBackrefs.add(obj, m_ScriptBackrefsNext); m_ScriptBackrefsNext++; // Return a non-tag number so callers know they need to serialize the object