Skip to content

Commit

Permalink
Add partial tests for import defer (#4278)
Browse files Browse the repository at this point in the history
Co-authored-by: Asumu Takikawa <[email protected]>
  • Loading branch information
nicolo-ribaudo and takikawa authored Dec 2, 2024
1 parent 80d32d1 commit 10c2615
Show file tree
Hide file tree
Showing 82 changed files with 1,728 additions and 0 deletions.
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,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: >
Deferred namespace objects have the correct MOP implementation
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, compareArray.js]
---*/

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

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

assert(!Reflect.isExtensible(ns), "Deferred namespaces are not extensible");
assert.sameValue(Reflect.preventExtensions(ns), true, "Deferred namespaces can made non-extensible");

assert.sameValue(Reflect.getPrototypeOf(ns), null, "Deferred namespaces have a null prototype");
assert.sameValue(Reflect.setPrototypeOf(ns, {}), false, "Deferred namespaces' prototype cannot be changed");
assert.sameValue(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, [], ns), "Deferred namespaces are not constructable");

assert.compareArray(
Reflect.ownKeys(ns),
["bar", "foo", Symbol.toStringTag],
"Deferred namespaces' keys are the exports sorted alphabetically, followed by @@toStringTag"
);

verifyProperty(ns, "foo", {
value: 1,
writable: true,
enumerable: true,
configurable: false,
});
assert.sameValue(Reflect.getOwnPropertyDescriptor(ns, "non-existent"), undefined, "No descriptors for non-exports");
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.sameValue(nsDeferred1, nsDeferred2, "Deferred import of the same module twice gives the same object");
assert.sameValue(nsDeferred1, nsDeferred3, "Deferred import of the same module twice from different files gives the same object");
assert.sameValue(nsDeferred1, nsDeferred4, "Static and dynamic deferred import of the same module gives the same object");
assert.notSameValue(nsDeferred1, nsEager, "Deferred namespaces are distinct from eager namespaces");
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, second } from "./promises_FIXTURE.js";
import "./dep-1.1_FIXTURE.js"

await Promise.resolve();

resolveFirst();

await second;

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,28 @@
// 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, resolveSecond } from "./promises_FIXTURE.js";
import defer * as ns from "./dep-1.1.1_FIXTURE.js";

// dep-1 is now in the ~evaluating~ state
try {
ns.foo;
} catch (error) {
globalThis["error on ns.foo while evaluating"] = error;
}

first.then(() => {
// dep-1 is now in the ~evaluating-async~ state
try {
ns.foo;
} catch (error) {
globalThis["error on ns.foo while evaluating-async"] = error;
}
resolveSecond();
}).then(() => {
return third.then(() => {
// dep-1 is now in the ~evaluated~ state
let foo = ns.foo;
globalThis["value of ns.foo when evaluated"] = foo;
})
}).then(resolveDone, rejectDone);
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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]
includes: [asyncHelpers.js]
---*/

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

asyncTest(async () => {
await done;
assert(globalThis["error on ns.foo while evaluating"] instanceof TypeError, "ns.foo while evaluating throws a TypeError");
assert(globalThis["error on ns.foo while evaluating-async"] instanceof TypeError, "ns.foo while evaluating-async throws a TypeError");
assert.sameValue(globalThis["value of ns.foo when evaluated"], 1);
});
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, rejectDone = j));

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,14 @@
// 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;

try {
dep2.foo;
} catch (error) {
globalThis["evaluating dep2.foo error"] = error;
}

globalThis["evaluating dep2.foo evaluates dep3"] = globalThis.dep3evaluated;
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,42 @@
// 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";

assert(globalThis["evaluating dep2.foo error"] instanceof TypeError, "evaluating dep2.foo throws a TypeError");
assert(!globalThis["evaluating dep2.foo evaluates dep3"], "evaluating dep2.foo does not evaluate dep3");
assert(!globalThis.dep3evaluated, "dep3 is not evaluated at all");
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, second } from "./promises_FIXTURE.js";
import "./dep-1.1_FIXTURE.js"

await Promise.resolve();

resolveFirst();

await second;

resolveThird();

export let foo = 1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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, resolveSecond, rejectDone, resolveDone } from "./promises_FIXTURE.js";
import defer * as ns from "./dep-1-tla_FIXTURE.js";

// ns is now in the ~evaluating~ state
try {
ns.foo;
} catch (error) {
globalThis["error on ns.foo while evaluating"] = error;
}

first.then(() => {
// ns is now in the ~evaluating-async~ state
try {
ns.foo;
} catch (error) {
globalThis["error on ns.foo while evaluating-async"] = error;
}
resolveSecond();
}).then(() => {
return third.then(() => {
// ns is now in the ~evaluated~ state
let foo = ns.foo;
globalThis["value of ns.foo when evaluated"] = foo;
})
}).then(resolveDone, rejectDone);
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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]
includes: [asyncHelpers.js]
---*/

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

asyncTest(async () => {
await done;
assert(globalThis["error on ns.foo while evaluating"] instanceof TypeError, "ns.foo while evaluating throws a TypeError");
assert(globalThis["error on ns.foo while evaluating-async"] instanceof TypeError, "ns.foo while evaluating-async throws a TypeError");
assert.sameValue(globalThis["value of ns.foo when evaluated"], 1);
});
Loading

0 comments on commit 10c2615

Please sign in to comment.