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

Conversation

nicolo-ribaudo
Copy link
Member

@nicolo-ribaudo nicolo-ribaudo commented Oct 25, 2024

Plan: #4215 (I checked the checkbox for tests added by this PR)

This PR includes tests for:

  • syntax
  • Deferred namespace objects
  • Behavior of synchronous and asynchronous deferred modules
  • Errors for synchronous and asynchronous cases

I will open another PR (or push another commit here) for tests about async modules, since I still have to finish polishing it up.

There are a few details of the proposal that are still in flux because of some bugs, so this PR does not include tests for them:

  • Symbol.toStringTag of deferred namespace objects
  • test/language/import/import-defer/evaluation-sync/evaluation-* for the other object operations
  • import.defer, which currently doesn't actually defer due to a problem with .then

@nicolo-ribaudo nicolo-ribaudo requested review from a team as code owners October 25, 2024 16:34
- syntax
- Deferred namespace objects
- Behavior of synchronous deferred modules
- Errors for synchronous cases

I will open another PR (or push another commit here) for tests about
async modules, since I still have to finish polishing it up.

There are a few details of the proposal that are still in flux because
of some bugs, so this PR does not include tests for them:
- `Symbol.toStringTag` of deferred namespace objects
- `test/language/import/import-defer/evaluation-sync/evaluation-*` for the other object operations
- `import.defer`, which currently doesn't actually defer due to a problem with `.then`
@takikawa
Copy link

I tried running these in my WebKit implementation and found a few minor issues in the tests, such as missing module flags. This commit has the fixes I made: takikawa/WebKit@d536aa2

After the fixes I had about 8 failures but at least some of these are due to bugs in my implementation, I think.

@nicolo-ribaudo
Copy link
Member Author

nicolo-ribaudo commented Oct 28, 2024

Thanks! I imported your patch.

After the fixes I had about 8 failures but at least some of these are due to bugs in my implementation, I think.

If you could share the list, I can double-check that the tests are correct.

(EDIT: Confirmed that they are bugs in Asumu's implementation)

  * Add deepEqual.js include where needed
  * Add module flag where needed
  * Fix some typos
  * In evaluation-ignore-set, add a try block to catch typeerrors from assignment

Need the bug URL (OOPS!).

Reviewed by NOBODY (OOPS!).

Explanation of why this fixes the bug (OOPS!).

* test/language/import/import-defer/deferred-namespace-object/dep-defer-ns_FIXTURE.js:
* test/language/import/import-defer/deferred-namespace-object/identity.js:
* test/language/import/import-defer/deferred-namespace-object/object-properties.js:
* test/language/import/import-defer/errors/get-other-while-dep-evaluating/main.js:
* test/language/import/import-defer/errors/get-other-while-evaluating/main.js:
* test/language/import/import-defer/errors/get-self-while-defer-evaluating/main.js:
* test/language/import/import-defer/errors/get-self-while-evaluating.js:
* test/language/import/import-defer/errors/module-throws/defer-import-after-evaluation.js:
* test/language/import/import-defer/errors/module-throws/third-party-evaluation-after-defer-import.js:
* test/language/import/import-defer/errors/module-throws/trigger-evaluation.js:
* test/language/import/import-defer/errors/resolution-error/import-defer-of-syntax-error-fails.js:
* test/language/import/import-defer/errors/syntax-error/import-defer-of-syntax-error-fails.js:
* test/language/import/import-defer/evaluation-sync/evaluation-ignore-get-symbol.js:
* test/language/import/import-defer/evaluation-sync/evaluation-ignore-getPrototypeOf.js:
* test/language/import/import-defer/evaluation-sync/evaluation-ignore-isExtensible.js:
* test/language/import/import-defer/evaluation-sync/evaluation-ignore-preventExtensions.js:
* test/language/import/import-defer/evaluation-sync/evaluation-ignore-set.js:
* test/language/import/import-defer/evaluation-sync/evaluation-ignore-setPrototypeOf.js:
* test/language/import/import-defer/evaluation-sync/evaluation-trigger-get-string.js:
* test/language/import/import-defer/evaluation-sync/import-defer-does-not-evaluate.js:
* test/language/import/import-defer/evaluation-sync/module-imported-defer-and-eager.js:
* test/language/import/import-defer/syntax/import-attributes.js:
* test/language/import/import-defer/syntax/valid-default-binding-named-defer.js:
* test/language/import/import-defer/syntax/valid-defer-namespace.js:
Copy link
Contributor

@ptomato ptomato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't gotten most of the way through this, but here are some initial comments.

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

import defer from "./dep_FIXTURE.js";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a test elsewhere (not in syntax) that ensures this syntax is treated as a sync default binding named defer and not as a deferred import? e.g. the imported module performs a side effect that is tested for.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Should I just add it in this file, given that we don't really have the concept of "syntax tests"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, that would be fine too. By "in syntax" I meant in this syntax/ folder where the test is currently placed; if the test no longer purely tests syntax, you could move it, but I don't think it matters that much.


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

await import("./throws_FIXTURE.js").catch(() => {});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my own understanding, the URIError should be thrown regardless of whether this line is present, right?

Or maybe not, since the testing plan says "import and import defer + exec only execute the module once"

In any case, you should probably have both a .then and a .catch callback, and assert that only one is executed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test for the following scenario:

  • you get a deferred namespace of a module that has not been evaluated yet
  • somebody else (in this case the dynamic import, but in the real world it will probably be a static import somewhere in another file) evaluates it. The test doesn't care about the details of this evaluation, just that it happens
  • you try to access the deferred namespace you originally got a hold of, and it re-throws the error cached by the other evaluation

In the plan, it's "Property access re-throws the evaluation error > On first access for already evaluated modules"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would put an assertion in the catch callback to indicate what's supposed to happen — either it should not be called, or it should be called with an error. (This applies to other places in the PR too.)

@nicolo-ribaudo
Copy link
Member Author

I noticed that I cannot use assert in _FIXTURE files (https://github.com/tc39/test262/blob/main/CONTRIBUTING.md#rules-for-module-_fixture-files). It's a bit weird because they are running in the same global scope as the non-fixture files, but I pushed a commit to fix it anyway.

Copy link
Contributor

@ptomato ptomato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! In general these tests are pretty easy to read and understand.

There were some things that I think are errors in assertions, like the tags pushed to globalThis.evaluations in some cases, so if your implementation is passing these tests in their current state, you might want to check if that's correct.

Comment on lines +34 to +37
assert(globalThis["error on ns.foo"] instanceof TypeError, "ns.foo while evaluating throws a TypeError");

ns.foo;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this order correct? It seems like the evaluation of the module should only happen during the evaluation of ns.foo in line 36, so in line 34 the error won't be present yet.
Additionally, do we not need to await Promise.resolve() in between evaluating ns.foo and checking for the TypeError, since the error will be caught in a microtask?

These comments may just be my misunderstanding of the proposal! I have similar comments on several other files which probably indicates that I'm missing some piece of comprehension.

(If I'm misunderstanding and microtasks in a deferred module import happen synchronously, then does this test and similar ones really need to be async though?)

Copy link
Member Author

@nicolo-ribaudo nicolo-ribaudo Nov 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is happening here is that ./dep_FIXTURE.js is not deferred, because it contains top-level await. So what is happening in this test is that:

  • the deferred module is not actually deferred, so dep_FIXTURE.js starts executing and goes in its evaluating state
  • it has access to a deferred namespace of itself
  • once it reaches the await, the state changes to evaluating-async
  • the test tries then to access a property from the deferred namespace while it's evaluating-async (which is what this test it testing). It should throw.
  • dep_FIXTURE.js is done, and becomes evaluated
  • main.js starts evaluating, and the error is already there
  • ns.foo now works, because ns is evaluated and not evaluating-async

Should I put this comment in the "info" section, even if usually it just lists spec algorithms?


assert.compareArray(
globalThis.evaluations,
["dep", "tla-with-dep start", "tla-with-dep end"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
["dep", "tla-with-dep start", "tla-with-dep end"]
["dep start", "tla-with-dep start", "tla-with-dep end"]

?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand how we end up with this result. dep start and tla-with-dep start I get, because they are sync dependencies of imports-tla-with-dep_FIXTURE.js. But isn't tla-with-dep end only pushed after microtasks execute?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed dep start to dep in dep_FIXTURE. As a rule, I'm using start/end for async modules (where the distinction is meaningful), and just the filename for sync modules.

I'm not sure I understand how we end up with this result. dep start and tla-with-dep start I get, because they are sync dependencies of imports-tla-with-dep_FIXTURE.js. But isn't tla-with-dep end only pushed after microtasks execute?

Due to the TLA, this file is equivalent to

import "./setup_FIXTURE.js";
import "./tla-with-dep_FIXTURE.js";
import defer * as ns from "./imports-tla-with-dep_FIXTURE.js";

assert.compareArray(
  globalThis.evaluations,
  ["dep", "tla-with-dep start", "tla-with-dep end"]
);
...

so main.js doesn't run until when tla-with-dep_FIXTURE.js is done running.

@nicolo-ribaudo
Copy link
Member Author

Thanks both for the reviews :) Comments addressed.

@nicolo-ribaudo
Copy link
Member Author

All fixed, thanks @takikawa!

@takikawa
Copy link

takikawa commented Nov 9, 2024

Aside from the flattening-order test which might need more adjustment, I ran the tests in JSC and saw 7 failures. I think these are likely all due to three existing issues with JSC's module loader (doesn't re-throw after exception during eval, module namespace object properties aren't writable, and certain cyclic module graphs cause debug assertions to fail) that aren't related to import defer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants