diff --git a/Userland/Libraries/LibJS/Runtime/ModuleNamespaceObject.cpp b/Userland/Libraries/LibJS/Runtime/ModuleNamespaceObject.cpp index 2fda0b0e9c1..2645a620c66 100644 --- a/Userland/Libraries/LibJS/Runtime/ModuleNamespaceObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/ModuleNamespaceObject.cpp @@ -210,16 +210,19 @@ ThrowCompletionOr ModuleNamespaceObject::internal_delete(PropertyKey const ThrowCompletionOr> ModuleNamespaceObject::internal_own_property_keys() const { // 1. Let exports be O.[[Exports]]. + // NOTE: We only add the exports after we know the size of symbolKeys + MarkedVector exports { vm().heap() }; // 2. Let symbolKeys be OrdinaryOwnPropertyKeys(O). auto symbol_keys = MUST(Object::internal_own_property_keys()); // 3. Return the list-concatenation of exports and symbolKeys. - for (auto& export_name : m_exports) { - symbol_keys.append(js_string(vm(), export_name)); - } + exports.ensure_capacity(m_exports.size() + symbol_keys.size()); + for (auto const& export_name : m_exports) + exports.unchecked_append(js_string(vm(), export_name)); + exports.extend(symbol_keys); - return symbol_keys; + return exports; } } diff --git a/Userland/Libraries/LibJS/Tests/modules/basic-modules.js b/Userland/Libraries/LibJS/Tests/modules/basic-modules.js index d0806c5e823..a9bd153b2cb 100644 --- a/Userland/Libraries/LibJS/Tests/modules/basic-modules.js +++ b/Userland/Libraries/LibJS/Tests/modules/basic-modules.js @@ -182,6 +182,10 @@ describe("in- and exports", () => { test("can import with (useless) assertions", () => { expectModulePassed("./import-with-assertions.mjs"); }); + + test("namespace has expected ordering", () => { + expectModulePassed("./namespace-order.mjs"); + }); }); describe("loops", () => { diff --git a/Userland/Libraries/LibJS/Tests/modules/namespace-order.mjs b/Userland/Libraries/LibJS/Tests/modules/namespace-order.mjs new file mode 100644 index 00000000000..d9a04144297 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/modules/namespace-order.mjs @@ -0,0 +1,16 @@ +import * as ns from "./default-and-star-export.mjs"; + +const keys = Reflect.ownKeys(ns); +// The keys should be in alphabetical order and @@toString at the end +if (keys.length < 4) throw new Error("Expected at least 3 keys and @@toStringTag"); + +if (keys[0] !== "") throw new Error('Expected keys[0] === ""'); + +if (keys[1] !== "*") throw new Error('Expected keys[1] === "*"'); + +if (keys[2] !== "default") throw new Error('Expected keys[2] === "default"'); + +if (keys.indexOf(Symbol.toStringTag) <= 2) + throw new Error("Expected Symbol.toStringTag to be behind string keys"); + +export const passed = true;