From 000fbcdea2166d79bbe0c1bc169bcb5da4f184e6 Mon Sep 17 00:00:00 2001 From: Rasmus Kromann-Larsen Date: Wed, 9 Sep 2026 11:59:34 +0200 Subject: [PATCH] xml: emit element attributes in sorted key order composeNode() walked node.attrib with pairs(), so attributes came out in string-hash order and the same tree could serialise differently between processes. Collect the keys, sort them, then emit. The non-string-key check moves into the collecting loop because table.sort cannot compare a string key against a non-string one; both error messages are unchanged. --- runtime/lua/xml.lua | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/runtime/lua/xml.lua b/runtime/lua/xml.lua index bfbe00be05a..dfb47138dd4 100644 --- a/runtime/lua/xml.lua +++ b/runtime/lua/xml.lua @@ -117,20 +117,27 @@ local function composeNode(frag, node, lvl) t_insert(frag, '<') t_insert(frag, node.elem) if node.attrib then + local attribKeys = { } for key, val in pairs(node.attrib) do if val then if type(key) ~= "string" then return "invalid xml tree (attribute name in <"..node.elem.."> is not a string)" - elseif type(val) ~= "string" then - return "invalid xml tree (value for attribute '"..key.."' in <"..node.elem.."> is not a string)" end - t_insert(frag, ' ') - t_insert(frag, key) - t_insert(frag, '="') - t_insert(frag, encodeContent(val)) - t_insert(frag, '"') + t_insert(attribKeys, key) end end + table.sort(attribKeys) + for _, key in ipairs(attribKeys) do + local val = node.attrib[key] + if type(val) ~= "string" then + return "invalid xml tree (value for attribute '"..key.."' in <"..node.elem.."> is not a string)" + end + t_insert(frag, ' ') + t_insert(frag, key) + t_insert(frag, '="') + t_insert(frag, encodeContent(val)) + t_insert(frag, '"') + end end if not node[1] then t_insert(frag, '/>\n')