Skip to content

Instantly share code, notes, and snippets.

@Yoric
Last active October 19, 2016 07:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Yoric/777effee02d6788d3abc639c82ff4488 to your computer and use it in GitHub Desktop.
Save Yoric/777effee02d6788d3abc639c82ff4488 to your computer and use it in GitHub Desktop.
Migrating from JSM to CommonJS

Even if we move to RequireJS modules, we still need to keep a version of Cu.import to avoid breaking Thunderbird and add-ons. Maintaining this version of Cu.import should also help with migrating some corner case modules, tests, etc.

The following steps are designed so that we can land them one at a time:

Progressively move from EXPORTED_SYMBOLS to exports

  1. Ensure our .jsm don't define a global exports or this.exports. I believe that the easiest way to do this is to patch mozJSComponentLoader to detect such globals and MOZ_ASSERT(false). Rewrite code that violates this invariant.
  2. Patch up mozJSComponentLoader to inject a global exports in loaded files.
  3. Patch up mozJSComponentLoader to use exports instead of EXPORTED_SYMBOLS if available.
  4. Code a rewriting script to migrate as many jsm, tests as possible to use exports automatically.
  5. Start to migrate manually code that hasn't migrated yet.
  6. (later) Eventually, at least in Firefox, start printing warnings for code that defines EXPORTED_SYMBOLS/this.EXPORTED_SYMBOLS.

Collapse mozilla-central compartments (not add-on compartments)

  1. Ensure our .jsm don't overwrite properties of globals (Object, String, ...). I'm not exactly sure how we can guarantee it, but I suspect that we can MOZ_ASSERT this in one of our many wrappers. Also, once we have static analysis running on JS, this can be moved to one of our linters.
  2. Patch up mozJSComponentLoader to always reuse the same compartment when there is no add-on id (optionally, we could also collapse to one compartment per add-on id, but I'm not sure it's worth the trouble). At this stage, in a module, code executed upon load by the mozJSComponentLoader is executed with the following semantics:
var initializer = new Function("exports", sourceCode);
var exports = {};
var self = { exports: exports };
initializer.bind(self)(exports);
Object.freeze(exports);

Note: at this stage, we still need a way to access BackstagePass.

Progressively move from Cu.import to require.

Note: This does not cover DevTools/Jetpack code, which relies upon an implementation of require. We probably should not touch this code. I assume that it's easy to make the difference.

Note 2: I have found exactly 2 uses of Cu.unload in our code, I hope that we can rewrite around them.

  1. Ensure our .jsm don't define a global require or this.require. I believe that the easiest way to do this is to patch mozJSComponentLoader to detect such globals and MOZ_ASSERT(false). Rewrite code that violates this invariant.
  2. Patch up mozJSComponentLoader so that Cu.import auto-exports code published through EXPORTED_SYMBOLS but not exports.
  3. Patch up mozJSComponentLoader to introduce a global function require with the following semantics:
function require(url) {
  Cu.import(url).exports
}
  1. Code a rewriting script to migrate as many jsm, tests as possible to use require automatically.
  2. Start to migrate manually code that hasn't migrated yet.
  3. (later) Eventually, at least in Firefox, start printing warnings for code that uses Cu.import.

Make static analysis work with lazy imports

  1. Patch up mozJSComponentLoader to introduce a function lazyRequire with the same semantics as defineLazyModuleGetter and the same signature as require.
  2. Rewrite our code to use lazyRequire instead of defineLazyModuleGetter.
  3. Either teach our static analysis tools that lazyRequire is require or introduce a rewriting step before static analysis to replace lazyRequire with require.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment