| Index: lib/src/codegen/js_codegen.dart
|
| diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart
|
| index ebfe8d27528addc34a8889e6d9e242a637ed802d..39b05cb6877099d2bbd4b4a575399ebb72cff31c 100644
|
| --- a/lib/src/codegen/js_codegen.dart
|
| +++ b/lib/src/codegen/js_codegen.dart
|
| @@ -5,7 +5,6 @@
|
| library dev_compiler.src.codegen.js_codegen;
|
|
|
| import 'dart:collection' show HashSet, HashMap;
|
| -import 'dart:io' show Directory, File;
|
|
|
| import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
|
| import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator;
|
| @@ -14,9 +13,6 @@ import 'package:analyzer/src/generated/element.dart';
|
| import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
|
| import 'package:analyzer/src/generated/scanner.dart'
|
| show StringToken, Token, TokenType;
|
| -import 'package:source_maps/source_maps.dart' as srcmaps show Printer;
|
| -import 'package:source_maps/source_maps.dart' show SourceMapSpan;
|
| -import 'package:source_span/source_span.dart' show SourceLocation;
|
| import 'package:path/path.dart' as path;
|
|
|
| import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder;
|
| @@ -31,8 +27,10 @@ import 'package:dev_compiler/src/options.dart';
|
| import 'package:dev_compiler/src/utils.dart';
|
|
|
| import 'code_generator.dart';
|
| -import 'js_names.dart';
|
| +import 'js_names.dart' show JSTemporary, invalidJSStaticMethodName;
|
| import 'js_metalet.dart';
|
| +import 'js_printer.dart' show writeJsLibrary;
|
| +import 'side_effect_analysis.dart';
|
|
|
| // Various dynamic helpers we call.
|
| // If renaming these, make sure to check other places like the
|
| @@ -1650,7 +1648,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
|
| Map<String, JS.Expression> scope, String name, Expression expr,
|
| {Expression context}) {
|
| // No need to do anything for stateless expressions.
|
| - if (_isStateless(expr, context)) return expr;
|
| + if (isStateless(expr, context)) return expr;
|
|
|
| var t = _createTemporary('#$name', expr.staticType);
|
| scope[name] = _visit(expr);
|
| @@ -2325,43 +2323,13 @@ class JSGenerator extends CodeGenerator {
|
| TypeProvider get types => rules.provider;
|
|
|
| String generateLibrary(LibraryUnit unit, LibraryInfo info) {
|
| - var jsTree =
|
| - new JSCodegenVisitor(info, rules, _extensionMethods).emitLibrary(unit);
|
| -
|
| - var outputPath = path.join(outDir, jsOutputPath(info, root));
|
| - new Directory(path.dirname(outputPath)).createSync(recursive: true);
|
| -
|
| - if (options.emitSourceMaps) {
|
| - var outFilename = path.basename(outputPath);
|
| - var printer = new srcmaps.Printer(outFilename);
|
| - _writeNode(
|
| - new SourceMapPrintingContext(printer, path.dirname(outputPath)),
|
| - jsTree);
|
| - printer.add('//# sourceMappingURL=$outFilename.map');
|
| - // Write output file and source map
|
| - var text = printer.text;
|
| - new File(outputPath).writeAsStringSync(text);
|
| - new File('$outputPath.map').writeAsStringSync(printer.map);
|
| - return computeHash(text);
|
| - } else {
|
| - var text = jsNodeToString(jsTree);
|
| - new File(outputPath).writeAsStringSync(text);
|
| - return computeHash(text);
|
| - }
|
| + var codegen = new JSCodegenVisitor(info, rules, _extensionMethods);
|
| + var module = codegen.emitLibrary(unit);
|
| + var dir = path.join(outDir, jsOutputPath(info, root));
|
| + return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps);
|
| }
|
| }
|
|
|
| -void _writeNode(JS.JavaScriptPrintingContext context, JS.Node node) {
|
| - var opts = new JS.JavaScriptPrintingOptions(allowKeywordsInProperties: true);
|
| - node.accept(new JS.Printer(opts, context, localNamer: new JSNamer(node)));
|
| -}
|
| -
|
| -String jsNodeToString(JS.Node node) {
|
| - var context = new JS.SimpleJavaScriptPrintingContext();
|
| - _writeNode(context, node);
|
| - return context.getText();
|
| -}
|
| -
|
| /// Choose a canonical name from the library element.
|
| /// This never uses the library's name (the identifier in the `library`
|
| /// declaration) as it doesn't have any meaningful rules enforced.
|
| @@ -2391,143 +2359,6 @@ String jsOutputPath(LibraryInfo info, Uri root) {
|
| return filepath;
|
| }
|
|
|
| -class SourceMapPrintingContext extends JS.JavaScriptPrintingContext {
|
| - final srcmaps.Printer printer;
|
| - final String outputDir;
|
| -
|
| - CompilationUnit unit;
|
| - Uri uri;
|
| -
|
| - SourceMapPrintingContext(this.printer, this.outputDir);
|
| -
|
| - void emit(String string) {
|
| - printer.add(string);
|
| - }
|
| -
|
| - void enterNode(JS.Node jsNode) {
|
| - AstNode node = jsNode.sourceInformation;
|
| - if (node is CompilationUnit) {
|
| - unit = node;
|
| - uri = _makeRelativeUri(unit.element.source.uri);
|
| - return;
|
| - }
|
| - if (unit == null || node == null || node.offset == -1) return;
|
| -
|
| - var loc = _location(node.offset);
|
| - var name = _getIdentifier(node);
|
| - if (name != null) {
|
| - // TODO(jmesserly): mark only uses the beginning of the span, but
|
| - // we're required to pass this as a valid span.
|
| - var end = _location(node.end);
|
| - printer.mark(new SourceMapSpan(loc, end, name, isIdentifier: true));
|
| - } else {
|
| - printer.mark(loc);
|
| - }
|
| - }
|
| -
|
| - SourceLocation _location(int offset) => locationForOffset(unit, uri, offset);
|
| -
|
| - Uri _makeRelativeUri(Uri src) {
|
| - return new Uri(path: path.relative(src.path, from: outputDir));
|
| - }
|
| -
|
| - void exitNode(JS.Node jsNode) {
|
| - AstNode node = jsNode.sourceInformation;
|
| - if (node is CompilationUnit) {
|
| - unit = null;
|
| - uri = null;
|
| - return;
|
| - }
|
| - if (unit == null || node == null || node.offset == -1) return;
|
| -
|
| - // TODO(jmesserly): in many cases marking the end will be unncessary.
|
| - printer.mark(_location(node.end));
|
| - }
|
| -
|
| - String _getIdentifier(AstNode node) {
|
| - if (node is SimpleIdentifier) return node.name;
|
| - return null;
|
| - }
|
| -}
|
| -
|
| -/// True is the expression can be evaluated multiple times without causing
|
| -/// code execution. This is true for final fields. This can be true for local
|
| -/// variables, if:
|
| -/// * they are not assigned within the [context].
|
| -/// * they are not assigned in a function closure anywhere.
|
| -/// True is the expression can be evaluated multiple times without causing
|
| -/// code execution. This is true for final fields. This can be true for local
|
| -/// variables, if:
|
| -///
|
| -/// * they are not assigned within the [context] scope.
|
| -/// * they are not assigned in a function closure anywhere.
|
| -///
|
| -/// This method is used to avoid creating temporaries in cases where we know
|
| -/// we can safely re-evaluate [node] multiple times in [context]. This lets
|
| -/// us generate prettier code.
|
| -///
|
| -/// This method is conservative: it should never return `true` unless it is
|
| -/// certain the [node] is stateless, because generated code may rely on the
|
| -/// correctness of a `true` value. However it may return `false` for things
|
| -/// that are in fact, stateless.
|
| -bool _isStateless(Expression node, [AstNode context]) {
|
| - if (node is SimpleIdentifier) {
|
| - var e = node.staticElement;
|
| - if (e is PropertyAccessorElement) e = e.variable;
|
| - if (e is VariableElement && !e.isSynthetic) {
|
| - if (e.isFinal) return true;
|
| - if (e is LocalVariableElement || e is ParameterElement) {
|
| - // make sure the local isn't mutated in the context.
|
| - return !_isPotentiallyMutated(e, context);
|
| - }
|
| - }
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -/// Returns true if the local variable is potentially mutated within [context].
|
| -/// This accounts for closures that may have been created outside of [context].
|
| -bool _isPotentiallyMutated(VariableElement e, [AstNode context]) {
|
| - if (e.isPotentiallyMutatedInClosure) return true;
|
| - if (e.isPotentiallyMutatedInScope) {
|
| - // Need to visit the context looking for assignment to this local.
|
| - if (context != null) {
|
| - var visitor = new _AssignmentFinder(e);
|
| - context.accept(visitor);
|
| - return visitor._potentiallyMutated;
|
| - }
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -/// Adapted from VariableResolverVisitor. Finds an assignment to a given
|
| -/// local variable.
|
| -class _AssignmentFinder extends RecursiveAstVisitor {
|
| - final VariableElement _variable;
|
| - bool _potentiallyMutated = false;
|
| -
|
| - _AssignmentFinder(this._variable);
|
| -
|
| - @override
|
| - visitSimpleIdentifier(SimpleIdentifier node) {
|
| - // Ignore if qualified.
|
| - AstNode parent = node.parent;
|
| - if (parent is PrefixedIdentifier &&
|
| - identical(parent.identifier, node)) return;
|
| - if (parent is PropertyAccess &&
|
| - identical(parent.propertyName, node)) return;
|
| - if (parent is MethodInvocation &&
|
| - identical(parent.methodName, node)) return;
|
| - if (parent is ConstructorName) return;
|
| - if (parent is Label) return;
|
| -
|
| - if (node.inSetterContext() && node.staticElement == _variable) {
|
| - _potentiallyMutated = true;
|
| - }
|
| - }
|
| -}
|
| -
|
| // TODO(jmesserly): validate the library. See issue #135.
|
| bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName';
|
|
|
|
|