diff --git a/source/gui/Scripting/JSInterface_GUIProxy_impl.h b/source/gui/Scripting/JSInterface_GUIProxy_impl.h index 0140ae2ef6..86bf177f92 100644 --- a/source/gui/Scripting/JSInterface_GUIProxy_impl.h +++ b/source/gui/Scripting/JSInterface_GUIProxy_impl.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -244,11 +244,16 @@ bool JSI_GUIProxy::get(JSContext* cx, JS::HandleObject proxy, JS::HandleValue } else if (propName == "children") { - Script::CreateArray(rq, vp); - - for (size_t i = 0; i < e->m_Children.size(); ++i) - Script::SetPropertyInt(rq, vp, i, e->m_Children[i]); + JS::RootedValueVector children{rq.cx}; + for (const auto& child : e->m_Children) + { + JS::RootedValue rootedChild{rq.cx}; + Script::ToJSVal(rq, &rootedChild, child); + if (!children.append(rootedChild)) + throw std::runtime_error{"Append failed"}; + } + vp.set(JS::ObjectValue(*JS::NewArrayObject(rq.cx, children))); return true; } else if (propName == "name") diff --git a/source/lobby/XmppClient.cpp b/source/lobby/XmppClient.cpp index 97b08f3b8a..fb5124cded 100644 --- a/source/lobby/XmppClient.cpp +++ b/source/lobby/XmppClient.cpp @@ -573,9 +573,7 @@ void XmppClient::handleOOB(const gloox::JID&, const gloox::OOB&) */ JS::Value XmppClient::GUIGetPlayerList(const ScriptRequest& rq) { - JS::RootedValue ret(rq.cx); - Script::CreateArray(rq, &ret); - int j = 0; + JS::RootedValueVector players{rq.cx}; for (const std::pair& p : m_PlayerMap) { @@ -589,9 +587,10 @@ JS::Value XmppClient::GUIGetPlayerList(const ScriptRequest& rq) "rating", p.second.m_Rating, "role", p.second.m_Role); - Script::SetPropertyInt(rq, ret, j++, player); + if (!players.append(player)) + throw std::runtime_error{"Append failed"}; } - return ret; + return JS::ObjectValue(*JS::NewArrayObject(rq.cx, players)); } /** @@ -601,9 +600,7 @@ JS::Value XmppClient::GUIGetPlayerList(const ScriptRequest& rq) */ JS::Value XmppClient::GUIGetGameList(const ScriptRequest& rq) { - JS::RootedValue ret(rq.cx); - Script::CreateArray(rq, &ret); - int j = 0; + JS::RootedValueVector games{rq.cx}; const char* stats[] = { "name", "hostUsername", "hostJID", "state", "hasPassword", "nbp", "maxnbp", "players", "mapName", "niceMapName", "mapSize", "mapType", @@ -617,9 +614,10 @@ JS::Value XmppClient::GUIGetGameList(const ScriptRequest& rq) for (size_t i = 0; i < ARRAY_SIZE(stats); ++i) Script::SetProperty(rq, game, stats[i], t->findAttribute(stats[i])); - Script::SetPropertyInt(rq, ret, j++, game); + if (!games.append(game)) + throw std::runtime_error{"Append failed"}; } - return ret; + return JS::ObjectValue(*JS::NewArrayObject(rq.cx, games)); } /** @@ -629,9 +627,7 @@ JS::Value XmppClient::GUIGetGameList(const ScriptRequest& rq) */ JS::Value XmppClient::GUIGetBoardList(const ScriptRequest& rq) { - JS::RootedValue ret(rq.cx); - Script::CreateArray(rq, &ret); - int j = 0; + JS::RootedValueVector boardList{rq.cx}; const char* attributes[] = { "name", "rank", "rating" }; @@ -643,9 +639,10 @@ JS::Value XmppClient::GUIGetBoardList(const ScriptRequest& rq) for (size_t i = 0; i < ARRAY_SIZE(attributes); ++i) Script::SetProperty(rq, board, attributes[i], t->findAttribute(attributes[i])); - Script::SetPropertyInt(rq, ret, j++, board); + if (!boardList.append(board)) + throw std::runtime_error{"Append failed"}; } - return ret; + return JS::ObjectValue(*JS::NewArrayObject(rq.cx, boardList)); } /** @@ -655,9 +652,7 @@ JS::Value XmppClient::GUIGetBoardList(const ScriptRequest& rq) */ JS::Value XmppClient::GUIGetProfile(const ScriptRequest& rq) { - JS::RootedValue ret(rq.cx); - Script::CreateArray(rq, &ret); - int j = 0; + JS::RootedValueVector profileData{rq.cx}; const char* stats[] = { "player", "rating", "totalGamesPlayed", "highestRating", "wins", "losses", "rank" }; @@ -669,9 +664,10 @@ JS::Value XmppClient::GUIGetProfile(const ScriptRequest& rq) for (size_t i = 0; i < ARRAY_SIZE(stats); ++i) Script::SetProperty(rq, profile, stats[i], t->findAttribute(stats[i])); - Script::SetPropertyInt(rq, ret, j++, profile); + if (!profileData.append(profile)) + throw std::runtime_error{"Append failed"}; } - return ret; + return JS::ObjectValue(*JS::NewArrayObject(rq.cx, profileData)); } /***************************************************** @@ -735,14 +731,12 @@ JS::Value XmppClient::GuiPollNewMessages(const ScriptInterface& guiInterface) // Optimize for batch message processing that is more // performance demanding than processing a lone message. - JS::RootedValue messages(rq.cx); - Script::CreateArray(rq, &messages); - - int j = 0; + JS::RootedValueVector messages{rq.cx}; for (const JS::Heap& message : m_GuiMessageQueue) { - Script::SetPropertyInt(rq, messages, j++, message); + if (!messages.append(message)) + throw std::runtime_error{"Append failed"}; // Store historic chat messages. // Only store relevant messages to minimize memory footprint. @@ -770,7 +764,8 @@ JS::Value XmppClient::GuiPollNewMessages(const ScriptInterface& guiInterface) m_GuiMessageQueue.clear(); // Copy the messages over to the caller script interface. - return Script::CloneValueFromOtherCompartment(guiInterface, *m_ScriptInterface, messages); + return Script::CloneValueFromOtherCompartment(guiInterface, *m_ScriptInterface, + JS::RootedValue{rq.cx, JS::ObjectValue(*JS::NewArrayObject(rq.cx, messages))}); } JS::Value XmppClient::GuiPollHistoricMessages(const ScriptInterface& guiInterface) @@ -780,15 +775,17 @@ JS::Value XmppClient::GuiPollHistoricMessages(const ScriptInterface& guiInterfac ScriptRequest rq(m_ScriptInterface); - JS::RootedValue messages(rq.cx); - Script::CreateArray(rq, &messages); + JS::RootedValueVector messages{rq.cx}; - int j = 0; for (const JS::Heap& message : m_HistoricGuiMessages) - Script::SetPropertyInt(rq, messages, j++, message); + { + if (!messages.append(message)) + throw std::runtime_error{"Append failed"}; + } // Copy the messages over to the caller script interface. - return Script::CloneValueFromOtherCompartment(guiInterface, *m_ScriptInterface, messages); + return Script::CloneValueFromOtherCompartment(guiInterface, *m_ScriptInterface, + JS::RootedValue{rq.cx, JS::ObjectValue(*JS::NewArrayObject(rq.cx, messages))}); } /** diff --git a/source/network/tests/test_NetMessage.h b/source/network/tests/test_NetMessage.h index ac94421886..48aa78d2b0 100644 --- a/source/network/tests/test_NetMessage.h +++ b/source/network/tests/test_NetMessage.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -37,11 +37,9 @@ public: ScriptInterface script("Test", "Test", g_ScriptContext); ScriptRequest rq(script); - JS::RootedValue val(rq.cx); - Script::CreateArray(rq, &val); - Script::SetPropertyInt(rq, val, 0, 4); - - CSimulationMessage msg(script, 1, 2, 3, val); + JS::RootedValueArray<1> val{rq.cx, JS::ValueArray<1>{JS::NumberValue(4)}}; + CSimulationMessage msg(script, 1, 2, 3, + JS::RootedValue{rq.cx, JS::ObjectValue(*JS::NewArrayObject(rq.cx, val))}); TS_ASSERT_STR_EQUALS(msg.ToString(), "CSimulationMessage { m_Client: 1, m_Player: 2, m_Turn: 3, m_Data: [4] }"); size_t len = msg.GetSerializedLength(); diff --git a/source/ps/GameSetup/HWDetect.cpp b/source/ps/GameSetup/HWDetect.cpp index 45911a285b..6c3197856f 100644 --- a/source/ps/GameSetup/HWDetect.cpp +++ b/source/ps/GameSetup/HWDetect.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -193,14 +193,13 @@ JS::Value MakeFreeTypeReport(const ScriptRequest& rq) void ReportLibraries(const ScriptRequest& rq, JS::HandleValue settings) { - JS::RootedValue librariesSettings(rq.cx); - Script::CreateArray(rq, &librariesSettings); - int libraryCount = 0; + JS::RootedValueVector librariesSettings{rq.cx}; - auto appendLibrary = [&rq, &librariesSettings, &libraryCount](const JS::Value& librarySettings) + auto appendLibrary = [&rq, &librariesSettings](const JS::Value& librarySettings) { JS::RootedValue value(rq.cx, librarySettings); - Script::SetPropertyInt(rq, librariesSettings, libraryCount++, value); + if (!librariesSettings.append(value)) + throw std::runtime_error{"Append failed"}; }; appendLibrary(MakeSDLReport(rq)); @@ -230,7 +229,8 @@ void ReportLibraries(const ScriptRequest& rq, JS::HandleValue settings) .MakeReport()); #endif - Script::SetProperty(rq, settings, "libraries", librariesSettings); + Script::SetProperty(rq, settings, "libraries", + JS::RootedValue{rq.cx, JS::ObjectValue(*JS::NewArrayObject(rq.cx, librariesSettings))}); } void WriteSystemInfo(Renderer::Backend::IDevice* device, const utsname& un) diff --git a/source/ps/ProfileViewer.cpp b/source/ps/ProfileViewer.cpp index 72d07fe742..936193811c 100644 --- a/source/ps/ProfileViewer.cpp +++ b/source/ps/ProfileViewer.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -494,19 +494,23 @@ namespace for (size_t r = 0; r < table->GetNumberRows(); ++r) { - JS::RootedValue row(rq.cx); - Script::CreateArray(rq, &row); + JS::RootedValueVector row{rq.cx}; + if (!row.resize(columns.size() + 1)) + throw std::runtime_error{"Resize failed"}; Script::SetProperty(rq, data, table->GetCellText(r, 0).c_str(), row); if (table->GetChild(r)) { JS::RootedValue childRows(rq.cx, DumpRows(table->GetChild(r))); - Script::SetPropertyInt(rq, row, 0, childRows); + row[0].set(childRows); } for (size_t c = 1; c < columns.size(); ++c) - Script::SetPropertyInt(rq, row, c, table->GetCellText(r, c)); + { + row[c].set(JS::StringValue( + JS_NewStringCopyZ(rq.cx, table->GetCellText(r, c).c_str()))); + } } return data; diff --git a/source/ps/SavedGame.cpp b/source/ps/SavedGame.cpp index 6e7ea7bc1b..2be85dbf35 100644 --- a/source/ps/SavedGame.cpp +++ b/source/ps/SavedGame.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -279,8 +279,7 @@ JS::Value SavedGames::GetSavedGames(const ScriptInterface& scriptInterface) PROFILE2("GetSavedGames"); ScriptRequest rq(scriptInterface); - JS::RootedValue games(rq.cx); - Script::CreateArray(rq, &games); + JS::RootedValueVector games{rq.cx}; Status err; @@ -288,10 +287,10 @@ JS::Value SavedGames::GetSavedGames(const ScriptInterface& scriptInterface) err = vfs::GetPathnames(g_VFS, "saves/", L"*.0adsave", pathnames); WARN_IF_ERR(err); - for (size_t i = 0; i < pathnames.size(); ++i) + for (const VfsPath& pathname : pathnames) { OsPath realPath; - err = g_VFS->GetRealPath(pathnames[i], realPath); + err = g_VFS->GetRealPath(pathname, realPath); if (err < 0) { DEBUG_WARN_ERR(err); @@ -319,13 +318,14 @@ JS::Value SavedGames::GetSavedGames(const ScriptInterface& scriptInterface) Script::CreateObject( rq, &game, - "id", pathnames[i].Basename(), + "id", pathname.Basename(), "metadata", metadata); - Script::SetPropertyInt(rq, games, i, game); + if (!games.append(game)) + throw std::runtime_error{"Append failed"}; } - return games; + return JS::ObjectValue(*JS::NewArrayObject(rq.cx, games)); } bool SavedGames::DeleteSavedGame(const std::wstring& name) diff --git a/source/ps/VisualReplay.cpp b/source/ps/VisualReplay.cpp index 61c1a09e70..196176c26a 100644 --- a/source/ps/VisualReplay.cpp +++ b/source/ps/VisualReplay.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -255,19 +255,20 @@ JS::Value VisualReplay::GetReplays(const ScriptInterface& scriptInterface, bool ScriptRequest rq(scriptInterface); JS::RootedObject replays(rq.cx, ReloadReplayCache(scriptInterface, compareFiles)); // Only take entries with data - JS::RootedValue replaysWithoutNullEntries(rq.cx); - Script::CreateArray(rq, &replaysWithoutNullEntries); + JS::RootedValueVector replaysWithoutNullEntries{rq.cx}; u32 replaysLength = 0; JS::GetArrayLength(rq.cx, replays, &replaysLength); - for (u32 j = 0, i = 0; j < replaysLength; ++j) + for (u32 j = 0; j < replaysLength; ++j) { JS::RootedValue replay(rq.cx); JS_GetElement(rq.cx, replays, j, &replay); - if (Script::HasProperty(rq, replay, "attribs")) - Script::SetPropertyInt(rq, replaysWithoutNullEntries, i++, replay); + if (!Script::HasProperty(rq, replay, "attribs")) + continue; + if (!replaysWithoutNullEntries.append(replay)) + throw std::runtime_error{"Append failed"}; } - return replaysWithoutNullEntries; + return JS::ObjectValue(*JS::NewArrayObject(rq.cx, replaysWithoutNullEntries)); } /** diff --git a/source/ps/scripting/JSInterface_ModIo.cpp b/source/ps/scripting/JSInterface_ModIo.cpp index e4e8381a7f..dfaa18a666 100644 --- a/source/ps/scripting/JSInterface_ModIo.cpp +++ b/source/ps/scripting/JSInterface_ModIo.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -70,10 +70,10 @@ JS::Value GetMods(const ScriptRequest& rq) const std::vector& availableMods = g_ModIo->GetMods(); - JS::RootedValue mods(rq.cx); - Script::CreateArray(rq, &mods, availableMods.size()); + JS::RootedValueVector mods{rq.cx}; + if (!mods.reserve(availableMods.size())) + throw std::runtime_error{"Reserve failed"}; - u32 i = 0; for (const ModIoModData& mod : availableMods) { JS::RootedValue m(rq.cx); @@ -83,10 +83,11 @@ JS::Value GetMods(const ScriptRequest& rq) Script::SetProperty(rq, m, prop.first.c_str(), prop.second, true); Script::SetProperty(rq, m, "dependencies", mod.dependencies, true); - Script::SetPropertyInt(rq, mods, i++, m); + if (!mods.append(m)) + throw std::runtime_error{"Append failed"}; } - return mods; + return JS::ObjectValue(*JS::NewArrayObject(rq.cx, mods)); } const std::map statusStrings = { diff --git a/source/ps/scripting/JSInterface_VFS.cpp b/source/ps/scripting/JSInterface_VFS.cpp index 6cb140b968..cbc38a32e6 100644 --- a/source/ps/scripting/JSInterface_VFS.cpp +++ b/source/ps/scripting/JSInterface_VFS.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -227,21 +227,20 @@ JS::Value ReadFileLines(const ScriptRequest& rq, const std::wstring& filename) // split into array of strings (one per line) std::stringstream ss(contents); - JS::RootedValue line_array(rq.cx); - Script::CreateArray(rq, &line_array); + JS::RootedValueVector lineArray{rq.cx}; std::string line; - int cur_line = 0; while (std::getline(ss, line)) { // Decode each line as UTF-8 JS::RootedValue val(rq.cx); Script::ToJSVal(rq, &val, CStr(line).FromUTF8()); - Script::SetPropertyInt(rq, line_array, cur_line++, val); + if (!lineArray.append(val)) + throw std::runtime_error{"Append failed"}; } - return line_array; + return JS::ObjectValue(*JS::NewArrayObject(rq.cx, lineArray)); } // Return file contents parsed as a JS Object diff --git a/source/renderer/backend/vulkan/Device.cpp b/source/renderer/backend/vulkan/Device.cpp index bb012aa8ed..cf5f30d98b 100644 --- a/source/renderer/backend/vulkan/Device.cpp +++ b/source/renderer/backend/vulkan/Device.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -726,16 +726,19 @@ void CDevice::Report(const ScriptRequest& rq, JS::HandleValue settings) ReportAvailablePhysicalDevice(m_ChoosenDevice, rq, device); Script::SetProperty(rq, settings, "choosen_device", device); - JS::RootedValue availableDevices(rq.cx); - Script::CreateArray(rq, &availableDevices, m_AvailablePhysicalDevices.size()); - for (size_t index = 0; index < m_AvailablePhysicalDevices.size(); ++index) + JS::RootedValueVector availableDevices{rq.cx}; + if (!availableDevices.reserve(m_AvailablePhysicalDevices.size())) + throw std::runtime_error{"Reserve failed"}; + for (const SAvailablePhysicalDevice& d : m_AvailablePhysicalDevices) { JS::RootedValue device(rq.cx); Script::CreateObject(rq, &device); - ReportAvailablePhysicalDevice(m_AvailablePhysicalDevices[index], rq, device); - Script::SetPropertyInt(rq, availableDevices, index, device); + ReportAvailablePhysicalDevice(d, rq, device); + if (!availableDevices.append(device)) + throw std::runtime_error{"Append failed"}; } - Script::SetProperty(rq, settings, "available_devices", availableDevices); + Script::SetProperty(rq, settings, "available_devices", + JS::RootedValue{rq.cx, JS::ObjectValue(*JS::NewArrayObject(rq.cx, availableDevices))}); Script::SetProperty(rq, settings, "instance_extensions", m_InstanceExtensions); Script::SetProperty(rq, settings, "validation_layers", m_ValidationLayers); diff --git a/source/renderer/backend/vulkan/DeviceSelection.cpp b/source/renderer/backend/vulkan/DeviceSelection.cpp index 25f4234034..3300c4a65a 100644 --- a/source/renderer/backend/vulkan/DeviceSelection.cpp +++ b/source/renderer/backend/vulkan/DeviceSelection.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -353,32 +354,38 @@ void ReportAvailablePhysicalDevice(const SAvailablePhysicalDevice& device, JS::RootedValue memory(rq.cx); Script::CreateObject(rq, &memory); - JS::RootedValue memoryTypes(rq.cx); - Script::CreateArray(rq, &memoryTypes, device.memoryProperties.memoryTypeCount); - for (uint32_t memoryTypeIndex = 0; memoryTypeIndex < device.memoryProperties.memoryTypeCount; ++memoryTypeIndex) + JS::RootedValueVector memoryTypes{rq.cx}; + if (!memoryTypes.reserve(device.memoryProperties.memoryTypeCount)) + throw std::runtime_error{"Reserve failed"}; + for (const VkMemoryType& type : std::span{device.memoryProperties.memoryTypes, + device.memoryProperties.memoryTypeCount}) { - const VkMemoryType& type = device.memoryProperties.memoryTypes[memoryTypeIndex]; JS::RootedValue memoryType(rq.cx); Script::CreateObject(rq, &memoryType); Script::SetProperty(rq, memoryType, "propertyFlags", static_cast(type.propertyFlags)); Script::SetProperty(rq, memoryType, "heapIndex", type.heapIndex); - Script::SetPropertyInt(rq, memoryTypes, memoryTypeIndex, memoryType); + if (!memoryTypes.append(memoryType)) + throw std::runtime_error{"Append failed"}; } - JS::RootedValue memoryHeaps(rq.cx); - Script::CreateArray(rq, &memoryHeaps, device.memoryProperties.memoryHeapCount); - for (uint32_t memoryHeapIndex = 0; memoryHeapIndex < device.memoryProperties.memoryHeapCount; ++memoryHeapIndex) + JS::RootedValueVector memoryHeaps{rq.cx}; + if (!memoryHeaps.reserve(device.memoryProperties.memoryHeapCount)) + throw std::runtime_error{"Reserve failed"}; + for (const VkMemoryHeap& heap : std::span{device.memoryProperties.memoryHeaps, + device.memoryProperties.memoryHeapCount}) { - const VkMemoryHeap& heap = device.memoryProperties.memoryHeaps[memoryHeapIndex]; JS::RootedValue memoryHeap(rq.cx); Script::CreateObject(rq, &memoryHeap); // We can't serialize uint64_t in JS, so put data in KiB. Script::SetProperty(rq, memoryHeap, "size", static_cast(heap.size / 1024)); Script::SetProperty(rq, memoryHeap, "flags", static_cast(heap.flags)); - Script::SetPropertyInt(rq, memoryHeaps, memoryHeapIndex, memoryHeap); + if (!memoryHeaps.append(memoryHeap)) + throw std::runtime_error{"Append failed"}; } - Script::SetProperty(rq, memory, "types", memoryTypes); - Script::SetProperty(rq, memory, "heaps", memoryHeaps); + Script::SetProperty(rq, memory, "types", + JS::RootedValue{rq.cx, JS::ObjectValue(*JS::NewArrayObject(rq.cx, memoryTypes))}); + Script::SetProperty(rq, memory, "heaps", + JS::RootedValue{rq.cx, JS::ObjectValue(*JS::NewArrayObject(rq.cx, memoryHeaps))}); Script::SetProperty(rq, settings, "memory", memory); JS::RootedValue constants(rq.cx); @@ -513,28 +520,33 @@ void ReportAvailablePhysicalDevice(const SAvailablePhysicalDevice& device, Script::SetProperty(rq, settings, "features", features); - JS::RootedValue presentModes(rq.cx); - Script::CreateArray(rq, &presentModes, device.presentModes.size()); - for (size_t index = 0; index < device.presentModes.size(); ++index) + JS::RootedValueVector presentModes{rq.cx}; + if (!presentModes.reserve(device.presentModes.size())) + throw std::runtime_error{"Reserve failed"}; + for (const VkPresentModeKHR& mode : device.presentModes) { - Script::SetPropertyInt( - rq, presentModes, index, static_cast(device.presentModes[index])); + if (!presentModes.append(JS::NumberValue(static_cast(mode)))) + throw std::runtime_error{"Append failed"}; } - Script::SetProperty(rq, settings, "present_modes", presentModes); + Script::SetProperty(rq, settings, "present_modes", + JS::RootedValue{rq.cx, JS::ObjectValue(*JS::NewArrayObject(rq.cx, presentModes))}); - JS::RootedValue surfaceFormats(rq.cx); - Script::CreateArray(rq, &surfaceFormats, device.surfaceFormats.size()); - for (size_t index = 0; index < device.surfaceFormats.size(); ++index) + JS::RootedValueVector surfaceFormats{rq.cx}; + if (!surfaceFormats.reserve(device.surfaceFormats.size())) + throw std::runtime_error{"Reserve failed"}; + for (const VkSurfaceFormatKHR& format : device.surfaceFormats) { JS::RootedValue surfaceFormat(rq.cx); Script::CreateObject(rq, &surfaceFormat); Script::SetProperty( - rq, surfaceFormat, "format", static_cast(device.surfaceFormats[index].format)); + rq, surfaceFormat, "format", static_cast(format.format)); Script::SetProperty( - rq, surfaceFormat, "color_space", static_cast(device.surfaceFormats[index].colorSpace)); - Script::SetPropertyInt(rq, surfaceFormats, index, surfaceFormat); + rq, surfaceFormat, "color_space", static_cast(format.colorSpace)); + if (!surfaceFormats.append(surfaceFormat)) + throw std::runtime_error{"Append failed"}; } - Script::SetProperty(rq, settings, "surface_formats", surfaceFormats); + Script::SetProperty(rq, settings, "surface_formats", + JS::RootedValue{rq.cx, JS::ObjectValue(*JS::NewArrayObject(rq.cx, surfaceFormats))}); JS::RootedValue surfaceCapabilities(rq.cx); Script::CreateObject(rq, &surfaceCapabilities); diff --git a/source/scriptinterface/Object.h b/source/scriptinterface/Object.h index f116d83e3a..2311869833 100644 --- a/source/scriptinterface/Object.h +++ b/source/scriptinterface/Object.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -271,15 +271,6 @@ inline bool CreateObject(const ScriptRequest& rq, JS::MutableHandleValue objectV return CreateObject(rq, objectValue, args...) && SetProperty(rq, objectValue, propertyName, val, false, true); } -/** - * Sets the given value to a new JS object or Null Value in case of out-of-memory. - */ -inline bool CreateArray(const ScriptRequest& rq, JS::MutableHandleValue objectValue, size_t length = 0) -{ - objectValue.setObjectOrNull(JS::NewArrayObject(rq.cx, length)); - return !objectValue.isNullOrUndefined(); -} - } // namespace Script #endif // INCLUDED_SCRIPTINTERFACE_Object diff --git a/source/simulation2/scripting/JSInterface_Simulation.cpp b/source/simulation2/scripting/JSInterface_Simulation.cpp index 02c4eaa68a..2eacc89fd9 100644 --- a/source/simulation2/scripting/JSInterface_Simulation.cpp +++ b/source/simulation2/scripting/JSInterface_Simulation.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -148,9 +148,7 @@ JS::Value GetEdgesOfStaticObstructionsOnScreenNearTo(const ScriptInterface& scri ENSURE(sim); ScriptRequest rq(scriptInterface); - JS::RootedValue edgeList(rq.cx); - Script::CreateArray(rq, &edgeList); - int edgeListIndex = 0; + JS::RootedValueVector edgeList{rq.cx}; const float distanceThreshold{g_ConfigDB.Get("gui.session.snaptoedgesdistancethreshold", 10.0f)}; CFixedVector2D entityPos(x, z); @@ -202,10 +200,11 @@ JS::Value GetEdgesOfStaticObstructionsOnScreenNearTo(const ScriptInterface& scri "normal", normal, "order", "cw"); - Script::SetPropertyInt(rq, edgeList, edgeListIndex++, edge); + if (!edgeList.append(edge)) + throw std::runtime_error{"Append failed"}; } } - return edgeList; + return JS::ObjectValue(*JS::NewArrayObject(rq.cx, edgeList)); } std::vector PickSimilarPlayerEntities(const std::string& templateName, bool includeOffScreen, bool matchRank, bool allowFoundations) diff --git a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp index d09d624658..079c2cbbff 100644 --- a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -163,14 +163,13 @@ QUERYHANDLER(GenerateMap) ScriptRequest rq(scriptInterface); // Set up 8-element array of empty objects to satisfy init - JS::RootedValue playerData(rq.cx); - Script::CreateArray(rq, &playerData); + JS::RootedValueArray<8> playerData{rq.cx}; - for (int i = 0; i < 8; ++i) + for (JS::Value& p : playerData.get().elements) { JS::RootedValue player(rq.cx); Script::CreateObject(rq, &player); - Script::SetPropertyInt(rq, playerData, i, player); + p = player; } JS::RootedValue settings(rq.cx); @@ -178,7 +177,8 @@ QUERYHANDLER(GenerateMap) rq, &settings, "mapType", "scenario", - "PlayerData", playerData); + "PlayerData", + JS::RootedValue{rq.cx, JS::ObjectValue(*JS::NewArrayObject(rq.cx, playerData))}); JS::RootedValue attrs(rq.cx); Script::CreateObject(