diff --git a/compiler/java/com/google/dart/compiler/backend/js/AbstractJsBackend.java b/compiler/java/com/google/dart/compiler/backend/js/AbstractJsBackend.java index 6a7ad8e21d4..e5477873574 100644 --- a/compiler/java/com/google/dart/compiler/backend/js/AbstractJsBackend.java +++ b/compiler/java/com/google/dart/compiler/backend/js/AbstractJsBackend.java @@ -13,6 +13,7 @@ import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.collect.Multiset; import com.google.dart.compiler.CommandLineOptions.CompilerOptions; +import com.google.common.io.Closeables; import com.google.dart.compiler.DartCompilerContext; import com.google.dart.compiler.DartSource; import com.google.dart.compiler.ast.DartClass; @@ -30,6 +31,8 @@ import com.google.dart.compiler.metrics.Tracer.TraceEvent; import com.google.dart.compiler.resolver.ClassElement; import com.google.dart.compiler.resolver.CoreTypeProvider; import com.google.dart.compiler.resolver.MethodElement; +import com.google.dart.compiler.util.DefaultTextOutput; +import com.google.dart.compiler.util.TextOutput; import java.io.IOException; import java.io.Writer; @@ -51,6 +54,11 @@ import java.util.Set; */ public abstract class AbstractJsBackend extends AbstractBackend { + public static final String EXTENSION_JS = "js"; + public static final String EXTENSION_APP_JS = "app.js"; + public static final String EXTENSION_JS_SRC_MAP = "js.map"; + public static final String EXTENSION_APP_JS_SRC_MAP = "app.js.map"; + private static final String ROOT_PART_NAME = ""; private static final String STATICS_PART_NAME = "$statics$"; private static final String SEPARATOR_PART_NAME = "$seperator$"; @@ -468,5 +476,74 @@ public abstract class AbstractJsBackend extends AbstractBackend { return mangler.mangleEntryPoint(entry, context.getApplicationUnit().getElement()); } + @Override + public boolean isOutOfDate(DartSource src, DartCompilerContext context) { + return context.isOutOfDate(src, src, EXTENSION_JS); + } + + @Override + public void compileUnit(DartUnit unit, DartSource src, DartCompilerContext context, + CoreTypeProvider typeProvider) throws IOException { + // Translate the AST to JS. + Map parts = translateToJS(unit, context, typeProvider); + String srcName = src.getName(); + + for (Map.Entry entry : parts.entrySet()) { + // Generate Javascript output. + TextOutput out = new DefaultTextOutput(false); + JsToStringGenerationVisitor srcGenerator; + String name = entry.getKey(); + boolean failed = true; + Writer w; + + JsProgram program = entry.getValue(); + JsBlock globalBlock = program.getGlobalBlock(); + + TraceEvent srcEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.JS_SOURCE_GEN, "src", srcName, "name", + name) : null; + try { + srcGenerator = new JsSourceGenerationVisitor(out); + + // TODO(johnlenz): Make source maps optional. + srcGenerator.generateSourceMap(true); + + srcGenerator.accept(globalBlock); + w = context.getArtifactWriter(src, name, EXTENSION_JS); + try { + w.write(out.toString()); + failed = false; + } finally { + Closeables.close(w, failed); + } + } finally { + Tracer.end(srcEvent); + } + + /* + * Currently, out of date checks require that we write a JS file even if it is empty. + * However, we should not write a map file if it is. + */ + if (!globalBlock.getStatements().isEmpty()) { + TraceEvent sourcemapEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.WRITE_SOURCE_MAP, "src", srcName, + "name", name) : null; + try { + // Write out the source map. + w = context.getArtifactWriter(src, name, EXTENSION_JS_SRC_MAP); + failed = true; + try { + srcGenerator.writeSourceMap(w, src.getName()); + failed = false; + } finally { + Closeables.close(w, failed); + } + } finally { + Tracer.end(sourcemapEvent); + } + } + } + } + protected abstract boolean shouldOptimize(); } diff --git a/compiler/java/com/google/dart/compiler/backend/js/ClosureJsAst.java b/compiler/java/com/google/dart/compiler/backend/js/ClosureJsAst.java index 577e80de4ee..0be722c625f 100644 --- a/compiler/java/com/google/dart/compiler/backend/js/ClosureJsAst.java +++ b/compiler/java/com/google/dart/compiler/backend/js/ClosureJsAst.java @@ -30,11 +30,14 @@ public class ClosureJsAst implements SourceAst { private final Source source; private final InputId inputId; - public ClosureJsAst(JsProgram program, String inputName, Source source) { + private final boolean validate; + + public ClosureJsAst(JsProgram program, String inputName, Source source, boolean validate) { assert(inputName != null); this.program = program; this.source = source; this.inputId = new InputId(inputName); + this.validate = validate; } @Override @@ -71,6 +74,6 @@ public class ClosureJsAst implements SourceAst { } private void createAst(AbstractCompiler compiler) { - root = new ClosureJsAstTranslator().translate(program, inputId, source); + root = new ClosureJsAstTranslator(validate).translate(program, inputId, source); } } diff --git a/compiler/java/com/google/dart/compiler/backend/js/ClosureJsAstTranslator.java b/compiler/java/com/google/dart/compiler/backend/js/ClosureJsAstTranslator.java index 160f40eb661..533e8b60f4e 100644 --- a/compiler/java/com/google/dart/compiler/backend/js/ClosureJsAstTranslator.java +++ b/compiler/java/com/google/dart/compiler/backend/js/ClosureJsAstTranslator.java @@ -78,6 +78,12 @@ public class ClosureJsAstTranslator { private final Map sourceCache = new HashMap(); + private final boolean validate; + + ClosureJsAstTranslator(boolean validate) { + this.validate = validate; + } + private StaticSourceFile getClosureSourceFile(Source source) { StaticSourceFile closureSourceFile = sourceCache.get(source); if (closureSourceFile == null) { @@ -97,7 +103,9 @@ public class ClosureJsAstTranslator { script.addChildToBack(transform(s)); } // Validate the structural integrity of the AST. - new AstValidator().validateScript(script); + if (validate) { + new AstValidator().validateScript(script); + } return script; } diff --git a/compiler/java/com/google/dart/compiler/backend/js/ClosureJsBackend.java b/compiler/java/com/google/dart/compiler/backend/js/ClosureJsBackend.java index 35681b6113f..c3dfe1d19b9 100644 --- a/compiler/java/com/google/dart/compiler/backend/js/ClosureJsBackend.java +++ b/compiler/java/com/google/dart/compiler/backend/js/ClosureJsBackend.java @@ -12,6 +12,7 @@ import com.google.common.collect.Sets; import com.google.common.io.CharStreams; import com.google.common.io.Closeables; import com.google.common.io.LimitInputStream; +import com.google.dart.compiler.CommandLineOptions; import com.google.dart.compiler.DartCompilationError; import com.google.dart.compiler.DartCompilerContext; import com.google.dart.compiler.DartSource; @@ -20,10 +21,16 @@ import com.google.dart.compiler.Source; import com.google.dart.compiler.ast.DartUnit; import com.google.dart.compiler.ast.LibraryNode; import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.backend.js.ast.JsBlock; import com.google.dart.compiler.backend.js.ast.JsProgram; import com.google.dart.compiler.common.SourceInfo; import com.google.dart.compiler.metrics.CompilerMetrics; +import com.google.dart.compiler.metrics.DartEventType; +import com.google.dart.compiler.metrics.Tracer; +import com.google.dart.compiler.metrics.Tracer.TraceEvent; import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.util.DefaultTextOutput; +import com.google.dart.compiler.util.TextOutput; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.CompilationLevel; import com.google.javascript.jscomp.Compiler; @@ -33,8 +40,12 @@ import com.google.javascript.jscomp.DiagnosticGroups; import com.google.javascript.jscomp.JSError; import com.google.javascript.jscomp.JSModule; import com.google.javascript.jscomp.JSSourceFile; +import com.google.javascript.jscomp.PropertyRenamingPolicy; import com.google.javascript.jscomp.Result; import com.google.javascript.jscomp.SourceAst; +import com.google.javascript.jscomp.SourceMap.DetailLevel; +import com.google.javascript.jscomp.SourceMap.Format; +import com.google.javascript.jscomp.VariableRenamingPolicy; import com.google.javascript.jscomp.WarningLevel; import java.io.IOException; @@ -58,8 +69,8 @@ import java.util.zip.ZipInputStream; * @author johnlenz@google.com (John Lenz) */ public class ClosureJsBackend extends AbstractJsBackend { - public static final String EXTENSION_JS = "opt.js"; - public static final String EXTENSION_JS_SRC_MAP = "opt.js.map"; + private static final String EXTENSION_OPT_JS = "opt.js"; + private static final String EXTENSION_OPT_JS_SRC_MAP = "opt.js.map"; // A map of possible input sources to use when building the optimized output. private Map dartSrcToUnitMap = Maps.newHashMap(); @@ -67,27 +78,58 @@ public class ClosureJsBackend extends AbstractJsBackend { // Generate "readable" output for debugging private final boolean generateHumanReadableOutput; + // Generate "good" instead of "best" output. + private final boolean fastOutput; + // TODO(johnlenz): Currently we can only support incremential builds + // if we aren't building source maps. + private final boolean incremental; + + private final boolean produceSourceMap; + // Validate the generated JavaScript + private final boolean validate; public ClosureJsBackend() { - this.generateHumanReadableOutput = false; + this(false); } /** * @param generateHumanReadableOutput - generates human readable javascript output. */ public ClosureJsBackend(boolean generateHumanReadableOutput) { + // Current default settings + this(false, true, false, true, generateHumanReadableOutput); + } + + public ClosureJsBackend(boolean fastOutput, + boolean produceSourceMap, + boolean incremental, + boolean validate, + boolean generateHumanReadableOutput) { + this.fastOutput = fastOutput; + this.produceSourceMap = produceSourceMap; + // can't currently produce a valid source map incrementally + this.incremental = incremental && !produceSourceMap; + this.validate = validate; this.generateHumanReadableOutput = generateHumanReadableOutput; } @Override public boolean isOutOfDate(DartSource src, DartCompilerContext context) { - return true; + if (!incremental) { + return true; + } else { + return super.isOutOfDate(src, context); + } } @Override public void compileUnit(DartUnit unit, DartSource src, - DartCompilerContext context, CoreTypeProvider typeProvider) { - dartSrcToUnitMap.put(src.getName(), unit); + DartCompilerContext context, CoreTypeProvider typeProvider) throws IOException { + if (!incremental) { + dartSrcToUnitMap.put(src.getName(), unit); + } else { + super.compileUnit(unit, src, context, typeProvider); + } } private Map createClosureJsAst(Map parts, Source source) { @@ -98,7 +140,7 @@ public class ClosureJsBackend extends AbstractJsBackend { for (Map.Entry part : parts.entrySet()) { String partName = part.getKey(); String inputName = name + ':' + partName; - SourceAst sourceAst = new ClosureJsAst(part.getValue(), inputName, source); + SourceAst sourceAst = new ClosureJsAst(part.getValue(), inputName, source, validate); CompilerInput input = new CompilerInput(sourceAst, false); translatedParts.put(part.getKey(), input); } @@ -131,21 +173,38 @@ public class ClosureJsBackend extends AbstractJsBackend { StringWriter w = new StringWriter(); Reader r = nativeSrc.getSourceReader(); CharStreams.copy(r, w); - inputs.add(new CompilerInput(JSSourceFile.fromCode(name, w.toString()), false)); + inputs.add(new CompilerInput(createSource(name, w), false)); } @Override - public void visitPart(Part part) { + public void visitPart(Part part) throws IOException { DartSource src = part.unit.getSource(); - Map translatedParts = translatedUnits.get(part.unit); - if (translatedParts == null) { - assert !sourcesByName.containsKey(src.getName()); - sourcesByName.put(src.getName(), src); - Preconditions.checkNotNull(part.unit, "src: " + src.getName()); - translatedParts = translateUnit(part.unit, src, context, typeProvider); - translatedUnits.put(part.unit, translatedParts); + if (!incremental) { + Map translatedParts = translatedUnits.get(part.unit); + if (translatedParts == null) { + assert !sourcesByName.containsKey(src.getName()); + sourcesByName.put(src.getName(), src); + Preconditions.checkNotNull(part.unit, "src: " + src.getName()); + translatedParts = translateUnit(part.unit, src, context, typeProvider); + Preconditions.checkState(!translatedUnits.containsKey(part.unit)); + translatedUnits.put(part.unit, translatedParts); + } + inputs.add(translatedParts.get(part.part)); + return; } - inputs.add(translatedParts.get(part.part)); + + Reader r = context.getArtifactReader(src, part.part, EXTENSION_JS); + if (r == null) { + return; + } + StringWriter w = new StringWriter(); + CharStreams.copy(r, w); + String inputName = src.getName() + ':' + part.part; + inputs.add(new CompilerInput(createSource(inputName, w), false)); + } + + private JSSourceFile createSource(String name, Writer w) { + return JSSourceFile.fromCode(name, w.toString()); } } @@ -162,7 +221,7 @@ public class ClosureJsBackend extends AbstractJsBackend { DependencyBuilder.build(context.getAppLibraryUnit(), callback); // Lastly, add the entry point. - inputs.add( getCompilerInputForEntry(context) ); + inputs.add(getCompilerInputForEntry(context)); // Currently, there is only a single module, add all the sources to it. JSModule mainModule = new JSModule("main"); @@ -172,10 +231,10 @@ public class ClosureJsBackend extends AbstractJsBackend { } } - Writer out = context.getArtifactWriter(app, "", EXTENSION_JS); + Writer out = context.getArtifactWriter(app, "", getAppExtension()); boolean failed = true; try { - Writer srcMapOut = context.getArtifactWriter(app, "", EXTENSION_JS_SRC_MAP); + Writer srcMapOut = context.getArtifactWriter(app, "", getSourceMapExtension()); boolean failed2 = true; try { compileModule( @@ -318,16 +377,15 @@ public class ClosureJsBackend extends AbstractJsBackend { Writer out, Writer srcMapOut) throws IOException { if (result.success) { + // TODO(johnlenz): Append directly to the writer. String output = compiler.toSource(module); out.append(output); out.append('\n'); - compiler.getSourceMap().appendTo(srcMapOut, module.getName()); - + if (produceSourceMap) { + compiler.getSourceMap().appendTo(srcMapOut, module.getName()); + } totalJsOutputCharCount = output.length(); - - // TODO(johnlenz): Output the externs declarations - // TODO(johnlenz): Output the manifest. } // return 0 if no errors, the error count otherwise diff --git a/compiler/java/com/google/dart/compiler/backend/js/JavascriptBackend.java b/compiler/java/com/google/dart/compiler/backend/js/JavascriptBackend.java index 6978aa0a33f..ae94333bc33 100644 --- a/compiler/java/com/google/dart/compiler/backend/js/JavascriptBackend.java +++ b/compiler/java/com/google/dart/compiler/backend/js/JavascriptBackend.java @@ -38,11 +38,6 @@ import java.util.Map; */ public class JavascriptBackend extends AbstractJsBackend { - public static final String EXTENSION_JS = "js"; - public static final String EXTENSION_APP_JS = "app.js"; - public static final String EXTENSION_JS_SRC_MAP = "js.map"; - public static final String EXTENSION_APP_JS_SRC_MAP = "app.js.map"; - /** * Wraps an Appendable and keeps track of the current offset as line/columns. */ @@ -180,75 +175,6 @@ public class JavascriptBackend extends AbstractJsBackend { } } - @Override - public boolean isOutOfDate(DartSource src, DartCompilerContext context) { - return context.isOutOfDate(src, src, EXTENSION_JS); - } - - @Override - public void compileUnit(DartUnit unit, DartSource src, DartCompilerContext context, - CoreTypeProvider typeProvider) throws IOException { - // Translate the AST to JS. - Map parts = translateToJS(unit, context, typeProvider); - String srcName = src.getName(); - - for (Map.Entry entry : parts.entrySet()) { - // Generate Javascript output. - TextOutput out = new DefaultTextOutput(false); - JsToStringGenerationVisitor srcGenerator; - String name = entry.getKey(); - boolean failed = true; - Writer w; - - JsProgram program = entry.getValue(); - JsBlock globalBlock = program.getGlobalBlock(); - - TraceEvent srcEvent = - Tracer.canTrace() ? Tracer.start(DartEventType.JS_SOURCE_GEN, "src", srcName, "name", - name) : null; - try { - srcGenerator = new JsSourceGenerationVisitor(out); - - // TODO(johnlenz): Make source maps optional. - srcGenerator.generateSourceMap(true); - - srcGenerator.accept(globalBlock); - w = context.getArtifactWriter(src, name, EXTENSION_JS); - try { - w.write(out.toString()); - failed = false; - } finally { - Closeables.close(w, failed); - } - } finally { - Tracer.end(srcEvent); - } - - /* - * Currently, out of date checks require that we write a JS file even if it is empty. - * However, we should not write a map file if it is. - */ - if (!globalBlock.getStatements().isEmpty()) { - TraceEvent sourcemapEvent = - Tracer.canTrace() ? Tracer.start(DartEventType.WRITE_SOURCE_MAP, "src", srcName, - "name", name) : null; - try { - // Write out the source map. - w = context.getArtifactWriter(src, name, EXTENSION_JS_SRC_MAP); - failed = true; - try { - srcGenerator.writeSourceMap(w, src.getName()); - failed = false; - } finally { - Closeables.close(w, failed); - } - } finally { - Tracer.end(sourcemapEvent); - } - } - } - } - @Override public void packageApp(LibrarySource app, Collection libraries,