mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 16:41:07 +00:00
Changes to allow the ClosureJsBackend to give people some options to play with:
- introduce a "fastDraft" mode that only runs the main code-removal passes and runs in about half the time and but produces code 2x the size (but still less than 10% of the original). - allows it to run in an quasi-incremential mode by consuming the incrementially generated js (cuts ~4s off the build time but source maps are useless). - allow source map generation to be turned off (saves a couple seconds). - turns off not useful check passes and makes the rest switchable. git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@91 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
30232ec2c0
commit
b2918972e8
|
@ -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<String, JsProgram> parts = translateToJS(unit, context, typeProvider);
|
||||
String srcName = src.getName();
|
||||
|
||||
for (Map.Entry<String, JsProgram> 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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,12 @@ public class ClosureJsAstTranslator {
|
|||
private final Map<Source, StaticSourceFile> sourceCache =
|
||||
new HashMap<Source, StaticSourceFile>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String, DartUnit> 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<String, CompilerInput> createClosureJsAst(Map<String,JsProgram> parts, Source source) {
|
||||
|
@ -98,7 +140,7 @@ public class ClosureJsBackend extends AbstractJsBackend {
|
|||
for (Map.Entry<String,JsProgram> 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<String, CompilerInput> 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<String, CompilerInput> 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
|
||||
|
|
|
@ -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<String, JsProgram> parts = translateToJS(unit, context, typeProvider);
|
||||
String srcName = src.getName();
|
||||
|
||||
for (Map.Entry<String, JsProgram> 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<LibraryUnit> libraries,
|
||||
|
|
Loading…
Reference in a new issue