Skip to content

Instantly share code, notes, and snippets.

@ChadKillingsworth
Last active August 29, 2015 14:20
Show Gist options
  • Save ChadKillingsworth/b86a4cffaa71571b5d01 to your computer and use it in GitHub Desktop.
Save ChadKillingsworth/b86a4cffaa71571b5d01 to your computer and use it in GitHub Desktop.
Closure-Compiler Export Design

Closure-Compiler Generated Exports

This document outlines the design for policies related to the @export annotation and the methodologies that Closure-compiler will use to generate such exports.

Closure Compiler export methodologies currently fall into two categories:

  • Shadowing: an alias to the symbol is created as either a property on the global object or as a variable in the global namespace.
  • Not Renaming: quoted properties are used to prevent the compiler from renaming a symbol.

The goal is to give the developer both a convenient and flexible option for exporting symbols which allows as many of the compiler checks and optimizations to apply as possible.

Current Methodologies for Exporting

Exports can currently be accomplished in several ways.

Export Using Helper Method(s) - Closure-Library

This methodology uses shadowing.

Uses a function call to accomplish the export.

var foo = {};
foo.bar = 42;
goog.exportSymbol('baz.foobar', foo.bar);

Advantages:

  • Makes it convenient for the exported name to be different and unrelated to the original code name.
  • Allows property exports to be immutable as the compiler can collapse the code property reference to a variable.
  • References to the name within the compiled code use a minified name.
  • Maximizes symbol mangling and thereby provides excellent protection of intellectual property.

Disadvantages:

Export By Not Renaming

Variables and properties are created using quoted properties. The code author is responsible to ensure that the quoted property is created in the propper scope.

window['foo'] = window['foo'] || {};
window['foo']['bar'] = 42;

Advantages:

  • Symbols used only externally are optimally defined.
  • Gives the developer the option to manually shadow the symbol for maximum flexibility.
  • Allows properties to be exported while the parent object is still renamed. This occurs frequently in Angular code patterns.
  • Requires no external code or additional methods to accomplish the export.
  • Gives the author maximum control of the namespace by avoiding aliasing.

Disadvantages:

  • References to the symbol within the compiled code use a uncompiled name. Developers can manually shadow the symbol to overcome this.
  • Exported properties are always mutable unless the developer manually exports an alias of the original property.
  • Some compiler checks and optimizations are blocked because the property is quoted.

Current Compiler Behavior

The Compiler currently uses a hybrid approach.

Most exports use the coding convention to indicate property and symbol export function names. Exports for these symbols use shadowing by making calls to the known functions which export the original name.

Uncompiled Code

/** @export */
var foo = {};

/** @export */
foo.bar = 42;

Compiled Code

var e = {};
goog.exportSymbol("foo", e); //goog.exportSymbol is renamed
e.a = 42;
goog.exportProperty("foo.bar", e.a); //goog.exportProperty is renamed

Local properties are exported by blocking renaming. This is implemented by creating synthetic extern properties on the Object type. Creating synthetic externs in this fashion blocks renaming of ANY property of the same name on unrelated objects. It is possible to modify this behaviour to create synthetic interfaces which classes with exports extend to mitigate some of this effect, although there are no remedies for anonymous objects.

Proposed Implementation

Convert @export to behave in a similar fashion to the local property exports of the current implementation. Synthetic extern interfaces will be created and the object marked as extending it.

Anonymous objects will require adding properties to the Object type. This blocks disambiguation of any property sharing that name.

Implementation steps

Shadow Policy

The existing GenerateExports pass would be modified to change behavior such that it treats every property as a local property. The pass would run by default and the command line flag would be removed.

@concavelenz
Copy link

I forget of we covered this but one of the reason for explicit exporting in the use of output wrappers. Without an explicit export, "export by rename prevention" variables would be hidden by the output wrapper.

@ChadKillingsworth
Copy link
Author

I've updated the document based on the last conversation we had. However, I am unsatisfied with the result.

The current implementation allows you to export a chain of properties and will create parent properties if they do not exist. I know of no equivalent way to do this when using a method that blocks renaming. This will break code implementation as it is a significant change in behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment