Skip to content

Commit

Permalink
Enable JsInterop for GWT version with a workaround for polluting the …
Browse files Browse the repository at this point in the history
…external

scope. Cleanup Flags usage to be more typesafe.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=130336797
  • Loading branch information
thorogood authored and blickly committed Aug 16, 2016
1 parent ee43373 commit dc877a8
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 83 deletions.
173 changes: 95 additions & 78 deletions src/com/google/javascript/jscomp/gwt/client/GwtRunner.java
Expand Up @@ -50,70 +50,99 @@ public final class GwtRunner implements EntryPoint {

private GwtRunner() {}

@JsType(namespace = JsPackage.GLOBAL, name = "Object", isNative = true)
private static class Flags {
boolean angularPass;
boolean assumeFunctionWrapper;
String compilationLevel;
boolean dartPass;
boolean exportLocalPropertyDefinitions;
boolean generateExports;
String languageIn;
String languageOut;
boolean checksOnly;
boolean newTypeInf;
boolean polymerPass;
boolean preserveTypeAnnotations;
boolean processCommonJsModules;
/**
* Specifies flags and their defaults.
*
* You must specify defaults in the constructor (as of August 2016). Defaults specified
* alongside fields will cause falsey values to be optimized and inlined. Fix here-
* https://gwt-review.googlesource.com/#/c/16600/
*/
@JsType(namespace = JsPackage.GLOBAL, name = "Flags")
public static class Flags {
public boolean angularPass;
public boolean assumeFunctionWrapper;
public String compilationLevel;
public boolean dartPass;
public boolean exportLocalPropertyDefinitions;
public boolean generateExports;
public String languageIn;
public String languageOut;
public boolean checksOnly;
public boolean newTypeInf;
public boolean polymerPass;
public boolean preserveTypeAnnotations;
public boolean processCommonJsModules;
public String renamePrefixNamespace;
boolean rewritePolyfills;
String warningLevel;
boolean useTypesForOptimization;

// These flags do not match the Java compiler JAR.
File[] jsCode;
File[] externs;
boolean createSourceMap;
public boolean rewritePolyfills;
public String warningLevel;
public boolean useTypesForOptimization;

// These flags do not match the Java jar release.
public File[] jsCode;
public File[] externs;
public boolean createSourceMap;

/**
* Flags constructor. Defaults must be specified here for every field.
*/
public Flags() {
this.angularPass = false;
this.assumeFunctionWrapper = false;
this.compilationLevel = "SIMPLE";
this.dartPass = false;
this.exportLocalPropertyDefinitions = false;
this.generateExports = false;
this.languageIn = "ES6";
this.languageOut = "ES5";
this.checksOnly = false;
this.newTypeInf = false;
this.polymerPass = false;
this.preserveTypeAnnotations = false;
this.processCommonJsModules = false;
this.renamePrefixNamespace = null;
this.rewritePolyfills = true;
this.warningLevel = "DEFAULT";
this.useTypesForOptimization = true;
this.jsCode = null;
this.externs = null;
this.createSourceMap = false;
}

/**
* Updates this {@link Flags} with a raw {@link JavaScriptObject}.
*
* @param raw The raw flags passed to this program.
* @return A list of invalid/unhandled flags.
*/
public native String[] update(JavaScriptObject raw) /*-{
var unhandled = [];
for (var k in raw) {
if (k in this) {
this[k] = raw[k];
} else {
unhandled.push(k);
}
}
return unhandled;
}-*/;
}

/**
* defaultFlags must have a value set for each field. Otherwise, GWT has no way to create the
* fields inside Flags (as it's native). If Flags is not-native, GWT eats its field names
* anyway.
* File object matching {@code AbstractCommandLineRunner.JsonFileSpec}. This is marked as the
* native {@code Object} type as it's not instantiated anywhere.
*/
private static final Flags defaultFlags = new Flags();
static {
defaultFlags.angularPass = false;
defaultFlags.assumeFunctionWrapper = false;
defaultFlags.compilationLevel = "SIMPLE";
defaultFlags.dartPass = false;
defaultFlags.exportLocalPropertyDefinitions = false;
defaultFlags.generateExports = false;
defaultFlags.languageIn = "ECMASCRIPT6";
defaultFlags.languageOut = "ECMASCRIPT5";
defaultFlags.checksOnly = false;
defaultFlags.newTypeInf = false;
defaultFlags.polymerPass = false;
defaultFlags.preserveTypeAnnotations = false;
defaultFlags.processCommonJsModules = false;
defaultFlags.renamePrefixNamespace = null;
defaultFlags.rewritePolyfills = true;
defaultFlags.warningLevel = "DEFAULT";
defaultFlags.useTypesForOptimization = true;
defaultFlags.jsCode = null;
defaultFlags.externs = null;
defaultFlags.createSourceMap = false;
}

@JsType(namespace = JsPackage.GLOBAL, name = "Object", isNative = true)
private static class File {
public static class File {
@JsProperty String path;
@JsProperty String src;
@JsProperty String sourceMap;
}

@JsType(namespace = JsPackage.GLOBAL, name = "Object", isNative = true)
private static class ModuleOutput {
/**
* Output type returned to caller.
*/
@JsType(namespace = JsPackage.GLOBAL, name = "ModuleOutput")
public static class ModuleOutput {
@JsProperty String compiledCode;
@JsProperty String sourceMap;
@JsProperty JavaScriptObject[] errors;
Expand Down Expand Up @@ -170,13 +199,16 @@ private static void applyOptionsFromFlags(CompilerOptions options, Flags flags)
warningLevel.setOptionsForWarningLevel(options);

LanguageMode languageIn = LanguageMode.fromString(flags.languageIn);
if (languageIn != null) {
options.setLanguageIn(languageIn);
if (languageIn == null) {
throw new RuntimeException("Bad value for languageIn: " + flags.languageIn);
}
options.setLanguageIn(languageIn);

LanguageMode languageOut = LanguageMode.fromString(flags.languageOut);
if (languageOut != null) {
options.setLanguageOut(languageOut);
if (languageOut == null) {
throw new RuntimeException("Bad value for languageOut: " + flags.languageOut);
}
options.setLanguageOut(languageOut);

if (flags.createSourceMap) {
options.setSourceMapOutputPath("%output%");
Expand Down Expand Up @@ -235,30 +267,15 @@ private static ImmutableMap<String, SourceMapInput> buildSourceMaps(
return inputSourceMaps.build();
}

/**
* Updates the destination flags (user input) with source flags (the defaults). Returns a list
* of flags that are on the destination, but not on the source.
*/
private static native String[] updateFlags(Flags dst, Flags src) /*-{
for (var k in src) {
if (!(k in dst)) {
dst[k] = src[k];
}
}
var unhandled = [];
for (var k in dst) {
if (!(k in src)) {
unhandled.push(k);
}
}
return unhandled;
}-*/;

/**
* Public compiler call. Exposed in {@link #exportCompile}.
*
* @param raw The passed raw flags from the user.
* @return The output from the compile.
*/
public static ModuleOutput compile(Flags flags) {
String[] unhandled = updateFlags(flags, defaultFlags);
public static ModuleOutput compile(JavaScriptObject raw) {
Flags flags = new Flags();
String[] unhandled = flags.update(raw);
if (unhandled.length > 0) {
throw new RuntimeException("Unhandled flag: " + unhandled[0]);
}
Expand Down
16 changes: 11 additions & 5 deletions src/com/google/javascript/jscomp/gwt/linker/MinimalLinker.java
Expand Up @@ -29,9 +29,11 @@
import com.google.gwt.dev.util.Util;

/**
* Simple single-script linker that doesn't add any dependence on the browser.
* This is intended to generate server-side runnable JS. It doesn't support
* permutations, nor does it allow late-loading code.
* Simple single-script linker that doesn't add any dependencies on the browser.
*
* This is intended to generate JS for servers, Node, or inside browsers as a module. It doesn't
* support permutations, nor does it allow late-loading code. It supports JSInterop, but it does not
* automatically add anything to the external scope (see {@code $wnd} below).
*/
@LinkerOrder(Order.PRIMARY)
public class MinimalLinker extends AbstractLinker {
Expand All @@ -41,10 +43,14 @@ public class MinimalLinker extends AbstractLinker {
* to invoke the {@code onModuleLoad} methods of loaded modules. The gwtOnLoad method then sets
* {@code $moduleName} and {@code $moduleBase}, shadow them here so they don't leak into the
* global scope.
*
* We also fake {@code $wnd} with an empty object (containg Error, to work around GWT using it
* statically in {@code StackTraceCreator}). JSInterop will export its objects here, which are
* then discarded at runtime. This is needed to avoid polluting our environments.
*/
private static final String PREFIX =
"(function(){var $wnd=this; var $doc={}; var $moduleName, $moduleBase;";
private static final String SUFFIX = "typeof gwtOnLoad==='function'&&gwtOnLoad()})();";
"(function(){var $wnd={Error:{}}; var $doc={}; var $moduleName, $moduleBase;";
private static final String SUFFIX = "$wnd=this;typeof gwtOnLoad==='function'&&gwtOnLoad()})();";

@Override
public String getDescription() {
Expand Down

0 comments on commit dc877a8

Please sign in to comment.