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

Support import assertions #37

Open
Jack-Works opened this issue Nov 26, 2020 · 4 comments
Open

Support import assertions #37

Jack-Works opened this issue Nov 26, 2020 · 4 comments

Comments

@Jack-Works
Copy link
Member

tc39/proposal-import-attributes#103

@kriskowal
Copy link
Member

Let’s add anoptions bag to compartment.import such that compartment.import(x, { assert: { type: 'json' } }), and add a type property to static module records that is "javascript" for objects constructed by new StaticModuleRecord. Then, let’s have the compartment verify that the asserted type of the import matches the purported type of the static module record.

Let’s expressly not thread { assert: { type } } into loadHook, since that courts use of the assertions decide what kind of record to return.

@kriskowal
Copy link
Member

cc @littledan

@kriskowal
Copy link
Member

I would note in the design rationales that we would not be able to enforce import assertions in a virtualized module loader if we did not encapsulate the linker.

@nicolo-ribaudo
Copy link
Member

nicolo-ribaudo commented Jul 16, 2022

Following the #71 design, I think import assertions fit nicely as new parameters of new Module.

NOTE: By "host" I mean either a real host or a loader/virtual host

The import assertions proposal has two goals:

  1. hosts can only know about the assertions they know how to handle
  2. import assertions must not affect how a module is loaded/executed, but they can only prevent a module from being executed (or from being "exposed" if it has already been executed)

while for (2) we can "trust" hosts to not make assertions affect module loading/execution (otherwise they are not ECMAScript-compliant), I think for loaders we must enforce it explicitly.

The Module constructor could accept two new parameters (or probably an options bag): new Module(source, importHook, importMeta, supportedAssertions, applyAssertions).

  • supportedAssertions is an array of strings, representing the assertion names supported by the host (HostGetSupportedImportAssertions() in the proposal)
  • applyAssertions(module: Module, assertions: { [name: string]: string }) is a function that throws an error if the imported module does not match the provided assertions
  • supportedAssertions and applyAssertions are both optional and default to [] and () => {}, but if you provide one you must provide the other

import would then behave like this:

class Module {
  #importHook; #importMeta; #supportedAssertions; #applyAssertions;

  #imports_cache = new Map();
  #load_deps_link_and_evaluate() { /* magic */ }

  async #import(specifier, { assert: inputAssertions = {} } = {}) {
    const resolvedAssertions = Object.create(null);
    for (const [key, value] of Object.entries(inputAssertions)) {
      if (supportedAssertions.includes(key)) resolvedAssertions[key] = value;
    }

    let module = this.#imports_cache.get(specifier);
    if (!module) {
      module = await importHook(specifier, importMeta);
      this.#imports_cache.set(specifier, module);
    }
    applyAssertions(module, resolvedAssertions);
    return module.#load_deps_link_and_evaluate();
  }
}

For example, if you wanted to implement a virtual host that only supports JavaScript and JSON modules, and that forces you to use type: "json" when importing JSON modules, you could do something like this:

async function loadHook(specifier, importMeta) {
  const source = /* magic loading */;
  const newImportMeta = /* magic logic */;
  return new Module(source, loadHook, newImportMeta, supportedAssertions, applyAssertions);
}
const supportedAssertions = ["type"];
function applyAssertions(module, assertions) {
  if (assertions.type === "json") {
    if (module.source instanceof JsonModuleSource) return;
    throw new TypeError("It's not a JSON module");
  }
  if (!assertions.type || assertions.type === "javascript") {
    if (module.source instanceof JavaScriptModuleSource) return;
    throw new TypeError("It's not a JavaScript module");
  }
  throw new TypeError("Unsupported type assertion: " + assertions.type);
}

// bootstrap
const entrypoint = new Module(entrypointSource, loadHook, entrypointImportMeta, supportedAssertions, applyAssertions);
await import(entrypoint);

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

No branches or pull requests

3 participants