Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

globalThis.evaluations.push("barrel");

export let direct = 1;
export defer { exported } from "./dep_FIXTURE.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-module-namespace-exotic-objects-get
description: >
Repeated ns.x access does not re-evaluate barrel or deferred dep
info: |
[[Get]] ( _P_, _Receiver_ )
1. ...
1. Let _m_ be _obj_.[[Module]].
1. If _m_ is a Cyclic Module Record and _m_.GetOptionalIndirectExportsModuleRequests(« _P_ »)
is not empty, then
1. Perform ? EvaluateModuleSync(_m_, « _P_ »).
1. Let _binding_ be _m_.ResolveExport(_P_).
1. ...
1. Let _targetEnv_ be _binding_.[[Module]].[[Environment]].
1. Return ? _targetEnv_.GetBindingValue(_binding_.[[BindingName]], *true*).

EvaluateModuleSync ( _module_ [ , _importedNames_ ] )
1. If _importedNames_ is not present, set _importedNames_ to « ».
1. If ReadyForSyncExecution(_module_, _importedNames_) is false, throw a TypeError exception.
1. Let _promise_ be _module_.Evaluate(_importedNames_).
1. Assert: _promise_.[[PromiseState]] is either ~fulfilled~ or ~rejected~.
1. ...

Evaluate ( _importedNames_ )
1. ...
1. If _importedNames_ is not present, set _importedNames_ to « ».
1. If _module_.[[Status]] is either ~evaluating-async~ or ~evaluated~, then
1. Assert: _module_.[[CycleRoot]].[[TopLevelCapability]] is not ~empty~.
1. Let _topLevelPromise_ be _module_.[[CycleRoot]].[[TopLevelCapability]].[[Promise]].
1. Else,
1. ...
1. ...

Once a module reaches ~evaluated~, Evaluate takes the first branch
and reuses the existing TopLevelCapability promise rather than
re-running InnerModuleEvaluation. Repeated [[Get]] of the same
deferred-reexported name therefore re-enters EvaluateModuleSync but
never re-executes any module body; GetBindingValue returns the same
already-initialized value.
flags: [module]
features: [export-defer, import-defer]
includes: [compareArray.js]
---*/

import "./setup_FIXTURE.js";
import defer * as ns from "./barrel_FIXTURE.js";

assert.compareArray(globalThis.evaluations, [],
"barrel and dep both deferred until first interaction with ns");

assert.sameValue(ns.exported, 3, "first read returns the binding's value");
assert.compareArray(globalThis.evaluations, ["barrel", "dep"],
"first ns.exported triggers barrel then dep evaluation");

assert.sameValue(ns.exported, 3, "second read returns the same value");
assert.compareArray(globalThis.evaluations, ["barrel", "dep"],
"second ns.exported does not re-evaluate either module");
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

globalThis.evaluations.push("dep");

export let exported = 3;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

globalThis.evaluations = [];
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-module-namespace-exotic-objects-get
description: >
ns.x triggers both barrel and deferred-dep evaluation under `import defer`
info: |
[[Get]] ( _P_, _Receiver_ )
1. ...
1. Let _m_ be _O_.[[Module]].
1. If _m_ is a Cyclic Module Record and _m_.GetOptionalIndirectExportsModuleRequests(« _P_ »)
is not empty, then
1. Perform ? EvaluateModuleSync(_m_, « _P_ »).
1. ...
flags: [module]
features: [export-defer, import-defer]
includes: [compareArray.js]
---*/

import "./setup_FIXTURE.js";
import defer * as ns from "./barrel_FIXTURE.js";

assert.compareArray(globalThis.evaluations, [],
"barrel and dep both deferred until first interaction with ns");

assert.sameValue(ns.exported, 3,
"deferred-reexported binding resolves to the original value");

assert.compareArray(globalThis.evaluations, ["barrel", "dep"],
"ns.exported triggers barrel evaluation, then dep evaluation");
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-module-namespace-exotic-objects-get
description: >
ns.direct triggers barrel evaluation only under `import defer`
info: |
[[Get]] ( _P_, _Receiver_ )
1. ...
1. Let _m_ be _O_.[[Module]].
1. If _m_ is a Cyclic Module Record and _m_.GetOptionalIndirectExportsModuleRequests(« _P_ »)
is not empty, then
1. Perform ? EvaluateModuleSync(_m_, « _P_ »).
1. ...

For names that are NOT deferred-reexported, GetOptionalIndirectExportsModuleRequests
returns an empty list, so EvaluateModuleSync is not called for them.
flags: [module]
features: [export-defer, import-defer]
includes: [compareArray.js]
---*/

import "./setup_FIXTURE.js";
import defer * as ns from "./barrel_FIXTURE.js";

assert.compareArray(globalThis.evaluations, [],
"barrel deferred until first interaction with ns");

assert.sameValue(ns.direct, 1,
"non-deferred binding resolves to the value defined in the barrel");

assert.compareArray(globalThis.evaluations, ["barrel"],
"ns.direct triggers barrel evaluation but not the deferred dep");
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-module-namespace-exotic-objects-hasproperty-p
description: >
in operator triggers barrel evaluation only under `import defer`
info: |
[[HasProperty]] ( _P_ )
1. If _P_ is a Symbol, return ! OrdinaryHasProperty(_O_, _P_).
1. Let _exports_ be _O_.[[Exports]].
1. If _exports_ contains _P_, return *true*.
1. Return *false*.

EvaluateModuleSync is only inserted into [[Get]]; [[HasProperty]] does
not route through [[Get]] and so does not trigger deferred-dep
evaluation, but it does interact with the deferred namespace and
therefore triggers barrel evaluation.
flags: [module]
features: [export-defer, import-defer]
includes: [compareArray.js]
---*/

import "./setup_FIXTURE.js";
import defer * as ns from "./barrel_FIXTURE.js";

assert.compareArray(globalThis.evaluations, [],
"barrel deferred until first interaction with ns");

assert.sameValue("exported" in ns, true,
"deferred-reexported name is reported as a member of the namespace");

assert.compareArray(globalThis.evaluations, ["barrel"],
"`in` triggers barrel evaluation but not the deferred dep");
Loading