From 520c489b68e65e40e0f404e9ae2bdb2bfbc9b6a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lancelot=20de=20Ferri=C3=A8re?= Date: Thu, 8 May 2025 14:52:28 +0200 Subject: [PATCH] Cache property keys during component serialization (cherry picked from commit ab6a420f78237ac3c86b9fe3a14147629c689302) (fixed build on this commit by moving a change to commit originally ea34960249c24f208895129baefbe1c50245db33) Signed-off-by: Itms --- .../serialization/BinarySerializer.cpp | 4 ++- .../serialization/BinarySerializer.h | 6 +++- .../serialization/SerializedScriptTypes.h | 33 +++++++++---------- .../serialization/StdDeserializer.cpp | 7 ++-- .../serialization/StdDeserializer.h | 4 ++- 5 files changed, 32 insertions(+), 22 deletions(-) diff --git a/source/simulation2/serialization/BinarySerializer.cpp b/source/simulation2/serialization/BinarySerializer.cpp index 4a966ef301..7c863f2348 100644 --- a/source/simulation2/serialization/BinarySerializer.cpp +++ b/source/simulation2/serialization/BinarySerializer.cpp @@ -62,6 +62,8 @@ CBinarySerializerScriptImpl::CBinarySerializerScriptImpl(const ScriptInterface& { ScriptRequest rq(m_ScriptInterface); JS_AddExtraGCRootsTracer(rq.cx, Trace, this); + m_SerializePropId = JS::PropertyKey::fromPinnedString(JS_AtomizeAndPinString(rq.cx, "Serialize")); + m_DeserializePropId = JS::PropertyKey::fromPinnedString(JS_AtomizeAndPinString(rq.cx, "Deserialize")); } CBinarySerializerScriptImpl::~CBinarySerializerScriptImpl() @@ -227,7 +229,7 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val) if (!JS_GetPrototype(rq.cx, obj, &proto)) throw PSERROR_Serialize_ScriptError("JS_GetPrototype failed"); - SPrototypeSerialization protoInfo = GetPrototypeInfo(rq, proto); + SPrototypeSerialization protoInfo = GetPrototypeInfo(rq, proto, m_SerializePropId, m_DeserializePropId); if (protoInfo.name == "Object") m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT); diff --git a/source/simulation2/serialization/BinarySerializer.h b/source/simulation2/serialization/BinarySerializer.h index 7ef08a874b..675fa0df3b 100644 --- a/source/simulation2/serialization/BinarySerializer.h +++ b/source/simulation2/serialization/BinarySerializer.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -98,6 +98,10 @@ private: ObjectTagMap m_ScriptBackrefTags; u32 m_ScriptBackrefsNext; u32 GetScriptBackrefTag(JS::HandleObject obj); + + JS::PropertyKey m_SerializePropId; + JS::PropertyKey m_DeserializePropId; + }; /** diff --git a/source/simulation2/serialization/SerializedScriptTypes.h b/source/simulation2/serialization/SerializedScriptTypes.h index 4a814ef500..b3447b6329 100644 --- a/source/simulation2/serialization/SerializedScriptTypes.h +++ b/source/simulation2/serialization/SerializedScriptTypes.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -63,7 +63,7 @@ struct SPrototypeSerialization bool hasNullSerialize = false; }; -inline SPrototypeSerialization GetPrototypeInfo(const ScriptRequest& rq, JS::HandleObject prototype) +inline SPrototypeSerialization GetPrototypeInfo(const ScriptRequest& rq, JS::HandleObject prototype, JS::PropertyKey ser, JS::PropertyKey deser) { SPrototypeSerialization ret; @@ -74,24 +74,23 @@ inline SPrototypeSerialization GetPrototypeInfo(const ScriptRequest& rq, JS::Han if (ret.name == "Object") return ret; - if (!JS_HasProperty(rq.cx, prototype, "Serialize", &ret.hasCustomSerialize) || - !JS_HasProperty(rq.cx, prototype, "Deserialize", &ret.hasCustomDeserialize)) - throw PSERROR_Serialize_ScriptError("JS_HasProperty failed"); + JS::RootedValue serialize(rq.cx); + if (!JS_GetPropertyById(rq.cx, prototype, JS::Handle::fromMarkedLocation(&ser), &serialize)) + throw PSERROR_Serialize_ScriptError("JS_GetProperty failed"); - if (ret.hasCustomSerialize) + if (serialize.isUndefined()) + return ret; + + ret.hasCustomSerialize = true; + if (serialize.isNull()) + ret.hasNullSerialize = true; + if (!JS_HasPropertyById(rq.cx, prototype, JS::Handle::fromMarkedLocation(&deser), &ret.hasCustomDeserialize) || + (!ret.hasNullSerialize && !ret.hasCustomDeserialize)) { - JS::RootedValue serialize(rq.cx); - if (!JS_GetProperty(rq.cx, prototype, "Serialize", &serialize)) - throw PSERROR_Serialize_ScriptError("JS_GetProperty failed"); - - if (serialize.isNull()) - ret.hasNullSerialize = true; - else if (!ret.hasCustomDeserialize) - { - // Don't throw for this error: mods might need updating and this crashes as exceptions are not correctly handled. - LOGERROR("Error serializing object '%s': non-null Serialize() but no matching Deserialize().", ret.name); - } + // Don't throw for this error: mods might need updating and this crashes as exceptions are not correctly handled. + LOGERROR("Error serializing object '%s': non-null Serialize() but no matching Deserialize().", ret.name); } + return ret; } diff --git a/source/simulation2/serialization/StdDeserializer.cpp b/source/simulation2/serialization/StdDeserializer.cpp index 5efb2a83a1..0b984062e2 100644 --- a/source/simulation2/serialization/StdDeserializer.cpp +++ b/source/simulation2/serialization/StdDeserializer.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -36,6 +36,9 @@ CStdDeserializer::CStdDeserializer(const ScriptInterface& scriptInterface, std:: m_ScriptInterface(scriptInterface), m_Stream(stream) { JS_AddExtraGCRootsTracer(ScriptRequest(scriptInterface).cx, CStdDeserializer::Trace, this); + m_SerializePropId = JS::PropertyKey::fromPinnedString(JS_AtomizeAndPinString(rq.cx, "Serialize")); + m_DeserializePropId = JS::PropertyKey::fromPinnedString(JS_AtomizeAndPinString(rq.cx, "Deserialize")); + // Insert a dummy object in front, as valid tags start at 1. m_ScriptBackrefs.emplace_back(nullptr); } @@ -168,7 +171,7 @@ JS::Value CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleOb JS::RootedObject prototype(rq.cx); JS_GetPrototype(rq.cx, obj, &prototype); - SPrototypeSerialization info = GetPrototypeInfo(rq, prototype); + SPrototypeSerialization info = GetPrototypeInfo(rq, prototype, m_SerializePropId, m_DeserializePropId); if (preexistingObject != nullptr && prototypeName != wstring_from_utf8(info.name)) throw PSERROR_Deserialize_ScriptError("Deserializer failed: incorrect pre-existing object"); diff --git a/source/simulation2/serialization/StdDeserializer.h b/source/simulation2/serialization/StdDeserializer.h index 75f2c61b45..b0abd15b0f 100644 --- a/source/simulation2/serialization/StdDeserializer.h +++ b/source/simulation2/serialization/StdDeserializer.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -52,6 +52,8 @@ private: virtual void AddScriptBackref(JS::HandleObject obj); virtual void GetScriptBackref(size_t tag, JS::MutableHandleObject ret); std::vector > m_ScriptBackrefs; + JS::PropertyKey m_SerializePropId; + JS::PropertyKey m_DeserializePropId; const ScriptInterface& m_ScriptInterface;