Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add partial tests for import defer #4278

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
4 changes: 4 additions & 0 deletions features.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ Atomics.pause
# https://github.com/tc39/proposal-is-error
Error.isError

# Deferred import evaluation
# https://tc39.es/proposal-defer-import-eval
import-defer

## Standard language features
#
# Language features that have been included in a published version of the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

import defer * as depDeferredNamespace from "./dep_FIXTURE.js";

export { depDeferredNamespace };
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

export const foo = 1;
export const bar = 2;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-getmodulenamespace
description: >
Deferred namespace objects are created and cached appropriately
info: |
GetModuleNamespace ( _module_, _phase_ )
1. ...
1. If _phase_ is ~defer~, let _namespace_ be _module_.[[DeferredNamespace]], otherwise let _namespace_ be _module_.[[Namespace]].
1. If _namespace_ is ~empty~, then
1. ...
1. Set _namespace_ to ModuleNamespaceCreate(_module_, _unambiguousNames_, _phase_).
1. Return _namespace_.

ModuleNamespaceCreate ( _module_, _exports_, _phase_ )
1. ...
1. Let _M_ be MakeBasicObject(_internalSlotsList_).
1. ...
1. If _phase_ is ~defer~, then
1. Set _module_.[[DeferredNamespace]] to _M_.
1. ...
1. Else,
1. Set _module_.[[Namespace]] to _M_.
1. ...
1. Return _M_.

flags: [module]
features: [import-defer]
---*/

import * as nsEager from "./dep_FIXTURE.js";

import defer * as nsDeferred1 from "./dep_FIXTURE.js";
import defer * as nsDeferred2 from "./dep_FIXTURE.js";
import { depDeferredNamespace as nsDeferred3 } from "./dep-defer-ns_FIXTURE.js";
const nsDeferred4 = await import.defer("./dep_FIXTURE.js");

assert(nsDeferred1 === nsDeferred2, "Deferred import of the same module twice gives the same object");
assert(nsDeferred1 === nsDeferred3, "Deferred import of the same module twice from different files gives the same object");
assert(nsDeferred1 === nsDeferred4, "Static and dynamic deferred import of the same module gives the same object");
assert(nsDeferred1 !== nsEager, "Deferred namespaces are distinct from eager namespaces");
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-modulenamespacecreate
description: >
`defer` is a valid name for default imports
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved
info: |
ModuleNamespaceCreate ( _module_, _exports_, _phase_ )
1. Let _internalSlotsList_ be the internal slots listed in <emu-xref href="#table-internal-slots-of-module-namespace-exotic-objects"></emu-xref>.
1. Let _M_ be MakeBasicObject(_internalSlotsList_).
1. Set _M_'s essential internal methods to the definitions specified in <emu-xref href="#sec-module-namespace-exotic-objects"></emu-xref>.
1. ...

[[GetPrototypeOf]] ( )
1. Return null.

[[IsExtensible]] ( )
1. Return false.

flags: [module]
features: [import-defer]
includes: [propertyHelper.js, deepEqual.js]
---*/

import defer * as ns from "./dep_FIXTURE.js";

assert(typeof ns === "object", "Deferred namespaces are objects");

assert(Reflect.isExtensible(ns) === false, "Deferred namespaces are not extensible");
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved
assert(Reflect.preventExtensions(ns) === true, "Deferred namespaces can made non-extensible");

assert(Reflect.getPrototypeOf(ns) === null, "Deferred namespaces have a null prototype");
assert(Reflect.setPrototypeOf(ns, {}) === false, "Deferred namespaces' prototype cannot be changed");
assert(Reflect.setPrototypeOf(ns, null) === true, "Deferred namespaces' prototype can be 'set' to null");

assert.throws(TypeError, () => Reflect.apply(ns, null, []), "Deferred namespaces are not callable");
assert.throws(TypeError, () => Reflect.construct(ns, null, []), "Deferred namespaces are not constructable");

assert.deepEqual(
Reflect.ownKeys(ns),
["bar", "foo", Symbol.toStringTag],
"Deferred namespaces' keys are the exports sorted alphabetically, followed by @@toStringTag"
);
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved

verifyProperty(ns, "foo", {
value: 1,
writable: true,
enumerable: true,
configurable: false,
});
assert(Reflect.getOwnPropertyDescriptor(ns, "non-existent") === undefined, "No descriptors for non-exports");
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

import { resolveFirst, resolveThird } from "./promises_FIXTURE.js";
import "./dep-1.1_FIXTURE.js"

await Promise.resolve();

resolveFirst();

await second;
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved

resolveThird();
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

import defer * as ns from "./dep-1-tla_FIXTURE.js";

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

import { first, third, rejectDone, resolveDone } from "./promises_FIXTURE.js";
import defer * as ns from "./dep-1.1.1-tla_FIXTURE.js";
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved

// dep-1 is now in the ~evaluating~ state
assert.throws(TypeError, () => ns.foo);

first.then(() => {
// dep-1 is now in the ~evaluating-async~ state
assert.throws(TypeError, () => ns.foo);
}).then(() => {
return third.then(() => {
// dep-1 is now in the ~evaluated~ state
let foo = ns.foo;
assert(foo === 1, "Once it finished evaluating, the module can be accessed");
})
}).then(resolveDone, rejectDone);
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (C) 2024 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-p-receiver-EnsureDeferredNamespaceEvaluation
description: >
Modules cannot try to trigger evaluation of modules that have dependencies in their ~evaluating-async~ phase
info: |
10.4.6.8 [[Get]] ( _P_, _Receiver_ )
1. ...
1. If _O_.[[Deferred]] is **true**, perform ? EnsureDeferredNamespaceEvaluation(_O_).
1. ...

EnsureDeferredNamespaceEvaluation ( _O_ )
1. Assert: _O_.[[Deferred]] is *false*.
1. Let _m_ be _O_.[[Module]].
1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception.
1. ...

ReadyForSyncExecution( _module_, _seen_ )
1. If _seen_ is not provided, let _seen_ be a new empty List.
1. If _seen_ contains _module_, return *true*.
1. Append _module_ to _seen_.
1. If _module_.[[Status]] is ~evaluated~, return *true*.
1. If _module_.[[Status]] is ~evaluating~ or ~evaluating-async~, return *false*.
1. Assert: _module_.[[Status]] is ~linked~.
1. If _module_.[[HasTLA]] is *true*, return *false*.
1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do
1. Let _requiredModule_ be GetImportedModule(_module_, _required_.[[Specifier]]).
1. If ReadyForSyncExecution(_requiredModule_, _seen_) is *false*, then
1. Return *false*.
1. Return *true*.

flags: [module, async]
features: [import-defer, top-level-await]
---*/

import { done } from "./promises_FIXTURE.js";
import "./dep-1-tla_FIXTURE.js";

done.then($DONE, $DONE);
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

export let resolveDone, rejectDone;
export const done = new Promise((r, j) => (resolveDone = r, rejecteDone = j));
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved

export let resolveFirst, rejectFirst;
export const first = new Promise((r, j) => (resolveFirst = r, rejectFirst = j));

export let resolveSecond, rejectSecond;
export const second = new Promise((r, j) => (resolveSecond = r, rejectSecond = j));

export let resolveThird, rejectThird;
export const third = new Promise((r, j) => (resolveThird = r, rejectThird = j));
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

import defer * as dep2 from "./dep-2_FIXTURE.js";

globalThis.dep3evaluated = false;
assert.throws(TypeError, () => dep2.foo);
assert(globalThis.dep3evaluated === false, "the 'evaluable' dependencies of dep-2 are not evaluated");
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

import "./dep-3_FIXTURE.js";
import "./main.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

globalThis.dep3evaluated = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (C) 2024 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-p-receiver-EnsureDeferredNamespaceEvaluation
description: >
Modules cannot try to trigger their own evaluation
info: |
10.4.6.8 [[Get]] ( _P_, _Receiver_ )
1. ...
1. If _O_.[[Deferred]] is **true**, perform ? EnsureDeferredNamespaceEvaluation(_O_).
1. ...

EnsureDeferredNamespaceEvaluation ( _O_ )
1. Assert: _O_.[[Deferred]] is *false*.
1. Let _m_ be _O_.[[Module]].
1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception.
1. ...

ReadyForSyncExecution( _module_, _seen_ )
1. If _seen_ is not provided, let _seen_ be a new empty List.
1. If _seen_ contains _module_, return *true*.
1. Append _module_ to _seen_.
1. If _module_.[[Status]] is ~evaluated~, return *true*.
1. If _module_.[[Status]] is ~evaluating~ or ~evaluating-async~, return *false*.
1. Assert: _module_.[[Status]] is ~linked~.
1. If _module_.[[HasTLA]] is *true*, return *false*.
1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do
1. Let _requiredModule_ be GetImportedModule(_module_, _required_.[[Specifier]]).
1. If ReadyForSyncExecution(_requiredModule_, _seen_) is *false*, then
1. Return *false*.
1. Return *true*.

flags: [module]
features: [import-defer]
---*/

import "./dep-1_FIXTURE.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

import { resolveFirst, resolveThird } from "./promises_FIXTURE.js";
import "./dep-1.1_FIXTURE.js"

await Promise.resolve();

resolveFirst();

await second;
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved

resolveThird();

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

import { first, third, rejectDone, resolveDone } from "./promises_FIXTURE.js";
import defer * as ns from "./dep-1-tla_FIXTURE.js";

// ns is now in the ~evaluating~ state
assert.throws(TypeError, () => ns.foo);

first.then(() => {
// ns is now in the ~evaluating-async~ state
assert.throws(TypeError, () => ns.foo);
}).then(() => {
return third.then(() => {
// ns is now in the ~evaluated~ state
let foo = ns.foo;
assert(foo === 1, "Once it finished evaluating, the module can be accessed");
})
}).then(resolveDone, rejectDone);
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (C) 2024 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-p-receiver-EnsureDeferredNamespaceEvaluation
description: >
Modules cannot try to trigger evaluation of modules that are in their ~evaluating-async~ phase
info: |
10.4.6.8 [[Get]] ( _P_, _Receiver_ )
1. ...
1. If _O_.[[Deferred]] is **true**, perform ? EnsureDeferredNamespaceEvaluation(_O_).
1. ...

EnsureDeferredNamespaceEvaluation ( _O_ )
1. Assert: _O_.[[Deferred]] is *false*.
1. Let _m_ be _O_.[[Module]].
1. If _m_ is a Cyclic Module Record, _m_.[[Status]] is not ~evaluated~, and ReadyForSyncExecution(_m_) is *false*, throw a *TypeError* exception.
1. ...

ReadyForSyncExecution( _module_, _seen_ )
1. If _seen_ is not provided, let _seen_ be a new empty List.
1. If _seen_ contains _module_, return *true*.
1. Append _module_ to _seen_.
1. If _module_.[[Status]] is ~evaluated~, return *true*.
1. If _module_.[[Status]] is ~evaluating~ or ~evaluating-async~, return *false*.
1. Assert: _module_.[[Status]] is ~linked~.
1. If _module_.[[HasTLA]] is *true*, return *false*.
1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do
1. Let _requiredModule_ be GetImportedModule(_module_, _required_.[[Specifier]]).
1. If ReadyForSyncExecution(_requiredModule_, _seen_) is *false*, then
1. Return *false*.
1. Return *true*.

flags: [module, async]
features: [import-defer, top-level-await]
---*/

import { done } from "./promises_FIXTURE.js";
import "./dep-1-tla_FIXTURE.js";

done.then($DONE, $DONE);
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

export let resolveDone, rejectDone;
export const done = new Promise((r, j) => (resolveDone = r, rejecteDone = j));
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved

export let resolveFirst, rejectFirst;
export const first = new Promise((r, j) => (resolveFirst = r, rejectFirst = j));

export let resolveSecond, rejectSecond;
export const second = new Promise((r, j) => (resolveSecond = r, rejectSecond = j));

export let resolveThird, rejectThird;
export const third = new Promise((r, j) => (resolveThird = r, rejectThird = j));
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

import defer * as main from "./main.js";

assert.throws(TypeError, () => main.foo);
Loading
Loading