Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 88b8462

Browse files
author
John Messerly
committed
[refactor] kick some stuff out of js_codegen.dart
the potentiallyMutated stuff goes into side_effect_analysis printing stuff goes into js_printer [email protected] Review URL: https://codereview.chromium.org/1062913004
1 parent 32fe994 commit 88b8462

File tree

3 files changed

+203
-177
lines changed

3 files changed

+203
-177
lines changed

lib/src/codegen/js_codegen.dart

Lines changed: 8 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
library dev_compiler.src.codegen.js_codegen;
66

77
import 'dart:collection' show HashSet, HashMap;
8-
import 'dart:io' show Directory, File;
98

109
import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
1110
import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator;
@@ -14,9 +13,6 @@ import 'package:analyzer/src/generated/element.dart';
1413
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
1514
import 'package:analyzer/src/generated/scanner.dart'
1615
show StringToken, Token, TokenType;
17-
import 'package:source_maps/source_maps.dart' as srcmaps show Printer;
18-
import 'package:source_maps/source_maps.dart' show SourceMapSpan;
19-
import 'package:source_span/source_span.dart' show SourceLocation;
2016
import 'package:path/path.dart' as path;
2117

2218
import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder;
@@ -31,8 +27,10 @@ import 'package:dev_compiler/src/options.dart';
3127
import 'package:dev_compiler/src/utils.dart';
3228

3329
import 'code_generator.dart';
34-
import 'js_names.dart';
30+
import 'js_names.dart' show JSTemporary, invalidJSStaticMethodName;
3531
import 'js_metalet.dart';
32+
import 'js_printer.dart' show writeJsLibrary;
33+
import 'side_effect_analysis.dart';
3634

3735
// Various dynamic helpers we call.
3836
// If renaming these, make sure to check other places like the
@@ -1650,7 +1648,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
16501648
Map<String, JS.Expression> scope, String name, Expression expr,
16511649
{Expression context}) {
16521650
// No need to do anything for stateless expressions.
1653-
if (_isStateless(expr, context)) return expr;
1651+
if (isStateless(expr, context)) return expr;
16541652

16551653
var t = _createTemporary('#$name', expr.staticType);
16561654
scope[name] = _visit(expr);
@@ -2325,43 +2323,13 @@ class JSGenerator extends CodeGenerator {
23252323
TypeProvider get types => rules.provider;
23262324

23272325
String generateLibrary(LibraryUnit unit, LibraryInfo info) {
2328-
var jsTree =
2329-
new JSCodegenVisitor(info, rules, _extensionMethods).emitLibrary(unit);
2330-
2331-
var outputPath = path.join(outDir, jsOutputPath(info, root));
2332-
new Directory(path.dirname(outputPath)).createSync(recursive: true);
2333-
2334-
if (options.emitSourceMaps) {
2335-
var outFilename = path.basename(outputPath);
2336-
var printer = new srcmaps.Printer(outFilename);
2337-
_writeNode(
2338-
new SourceMapPrintingContext(printer, path.dirname(outputPath)),
2339-
jsTree);
2340-
printer.add('//# sourceMappingURL=$outFilename.map');
2341-
// Write output file and source map
2342-
var text = printer.text;
2343-
new File(outputPath).writeAsStringSync(text);
2344-
new File('$outputPath.map').writeAsStringSync(printer.map);
2345-
return computeHash(text);
2346-
} else {
2347-
var text = jsNodeToString(jsTree);
2348-
new File(outputPath).writeAsStringSync(text);
2349-
return computeHash(text);
2350-
}
2326+
var codegen = new JSCodegenVisitor(info, rules, _extensionMethods);
2327+
var module = codegen.emitLibrary(unit);
2328+
var dir = path.join(outDir, jsOutputPath(info, root));
2329+
return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps);
23512330
}
23522331
}
23532332

2354-
void _writeNode(JS.JavaScriptPrintingContext context, JS.Node node) {
2355-
var opts = new JS.JavaScriptPrintingOptions(allowKeywordsInProperties: true);
2356-
node.accept(new JS.Printer(opts, context, localNamer: new JSNamer(node)));
2357-
}
2358-
2359-
String jsNodeToString(JS.Node node) {
2360-
var context = new JS.SimpleJavaScriptPrintingContext();
2361-
_writeNode(context, node);
2362-
return context.getText();
2363-
}
2364-
23652333
/// Choose a canonical name from the library element.
23662334
/// This never uses the library's name (the identifier in the `library`
23672335
/// declaration) as it doesn't have any meaningful rules enforced.
@@ -2391,143 +2359,6 @@ String jsOutputPath(LibraryInfo info, Uri root) {
23912359
return filepath;
23922360
}
23932361

2394-
class SourceMapPrintingContext extends JS.JavaScriptPrintingContext {
2395-
final srcmaps.Printer printer;
2396-
final String outputDir;
2397-
2398-
CompilationUnit unit;
2399-
Uri uri;
2400-
2401-
SourceMapPrintingContext(this.printer, this.outputDir);
2402-
2403-
void emit(String string) {
2404-
printer.add(string);
2405-
}
2406-
2407-
void enterNode(JS.Node jsNode) {
2408-
AstNode node = jsNode.sourceInformation;
2409-
if (node is CompilationUnit) {
2410-
unit = node;
2411-
uri = _makeRelativeUri(unit.element.source.uri);
2412-
return;
2413-
}
2414-
if (unit == null || node == null || node.offset == -1) return;
2415-
2416-
var loc = _location(node.offset);
2417-
var name = _getIdentifier(node);
2418-
if (name != null) {
2419-
// TODO(jmesserly): mark only uses the beginning of the span, but
2420-
// we're required to pass this as a valid span.
2421-
var end = _location(node.end);
2422-
printer.mark(new SourceMapSpan(loc, end, name, isIdentifier: true));
2423-
} else {
2424-
printer.mark(loc);
2425-
}
2426-
}
2427-
2428-
SourceLocation _location(int offset) => locationForOffset(unit, uri, offset);
2429-
2430-
Uri _makeRelativeUri(Uri src) {
2431-
return new Uri(path: path.relative(src.path, from: outputDir));
2432-
}
2433-
2434-
void exitNode(JS.Node jsNode) {
2435-
AstNode node = jsNode.sourceInformation;
2436-
if (node is CompilationUnit) {
2437-
unit = null;
2438-
uri = null;
2439-
return;
2440-
}
2441-
if (unit == null || node == null || node.offset == -1) return;
2442-
2443-
// TODO(jmesserly): in many cases marking the end will be unncessary.
2444-
printer.mark(_location(node.end));
2445-
}
2446-
2447-
String _getIdentifier(AstNode node) {
2448-
if (node is SimpleIdentifier) return node.name;
2449-
return null;
2450-
}
2451-
}
2452-
2453-
/// True is the expression can be evaluated multiple times without causing
2454-
/// code execution. This is true for final fields. This can be true for local
2455-
/// variables, if:
2456-
/// * they are not assigned within the [context].
2457-
/// * they are not assigned in a function closure anywhere.
2458-
/// True is the expression can be evaluated multiple times without causing
2459-
/// code execution. This is true for final fields. This can be true for local
2460-
/// variables, if:
2461-
///
2462-
/// * they are not assigned within the [context] scope.
2463-
/// * they are not assigned in a function closure anywhere.
2464-
///
2465-
/// This method is used to avoid creating temporaries in cases where we know
2466-
/// we can safely re-evaluate [node] multiple times in [context]. This lets
2467-
/// us generate prettier code.
2468-
///
2469-
/// This method is conservative: it should never return `true` unless it is
2470-
/// certain the [node] is stateless, because generated code may rely on the
2471-
/// correctness of a `true` value. However it may return `false` for things
2472-
/// that are in fact, stateless.
2473-
bool _isStateless(Expression node, [AstNode context]) {
2474-
if (node is SimpleIdentifier) {
2475-
var e = node.staticElement;
2476-
if (e is PropertyAccessorElement) e = e.variable;
2477-
if (e is VariableElement && !e.isSynthetic) {
2478-
if (e.isFinal) return true;
2479-
if (e is LocalVariableElement || e is ParameterElement) {
2480-
// make sure the local isn't mutated in the context.
2481-
return !_isPotentiallyMutated(e, context);
2482-
}
2483-
}
2484-
}
2485-
return false;
2486-
}
2487-
2488-
/// Returns true if the local variable is potentially mutated within [context].
2489-
/// This accounts for closures that may have been created outside of [context].
2490-
bool _isPotentiallyMutated(VariableElement e, [AstNode context]) {
2491-
if (e.isPotentiallyMutatedInClosure) return true;
2492-
if (e.isPotentiallyMutatedInScope) {
2493-
// Need to visit the context looking for assignment to this local.
2494-
if (context != null) {
2495-
var visitor = new _AssignmentFinder(e);
2496-
context.accept(visitor);
2497-
return visitor._potentiallyMutated;
2498-
}
2499-
return true;
2500-
}
2501-
return false;
2502-
}
2503-
2504-
/// Adapted from VariableResolverVisitor. Finds an assignment to a given
2505-
/// local variable.
2506-
class _AssignmentFinder extends RecursiveAstVisitor {
2507-
final VariableElement _variable;
2508-
bool _potentiallyMutated = false;
2509-
2510-
_AssignmentFinder(this._variable);
2511-
2512-
@override
2513-
visitSimpleIdentifier(SimpleIdentifier node) {
2514-
// Ignore if qualified.
2515-
AstNode parent = node.parent;
2516-
if (parent is PrefixedIdentifier &&
2517-
identical(parent.identifier, node)) return;
2518-
if (parent is PropertyAccess &&
2519-
identical(parent.propertyName, node)) return;
2520-
if (parent is MethodInvocation &&
2521-
identical(parent.methodName, node)) return;
2522-
if (parent is ConstructorName) return;
2523-
if (parent is Label) return;
2524-
2525-
if (node.inSetterContext() && node.staticElement == _variable) {
2526-
_potentiallyMutated = true;
2527-
}
2528-
}
2529-
}
2530-
25312362
// TODO(jmesserly): validate the library. See issue #135.
25322363
bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName';
25332364

lib/src/codegen/js_printer.dart

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
library dev_compiler.src.codegen.js_printer;
6+
7+
import 'dart:io' show Directory, File;
8+
import 'package:analyzer/src/generated/ast.dart';
9+
import 'package:path/path.dart' as path;
10+
import 'package:source_maps/source_maps.dart' as srcmaps show Printer;
11+
import 'package:source_maps/source_maps.dart' show SourceMapSpan;
12+
import 'package:source_span/source_span.dart' show SourceLocation;
13+
14+
import 'package:dev_compiler/src/js/js_ast.dart' as JS;
15+
import 'package:dev_compiler/src/utils.dart'
16+
show computeHash, locationForOffset;
17+
18+
import 'js_names.dart' show JSNamer;
19+
20+
String writeJsLibrary(JS.Program jsTree, String outputPath,
21+
{bool emitSourceMaps: false}) {
22+
new Directory(path.dirname(outputPath)).createSync(recursive: true);
23+
if (emitSourceMaps) {
24+
var outFilename = path.basename(outputPath);
25+
var printer = new srcmaps.Printer(outFilename);
26+
writeNodeToContext(jsTree,
27+
new SourceMapPrintingContext(printer, path.dirname(outputPath)));
28+
printer.add('//# sourceMappingURL=$outFilename.map');
29+
// Write output file and source map
30+
var text = printer.text;
31+
new File(outputPath).writeAsStringSync(text);
32+
new File('$outputPath.map').writeAsStringSync(printer.map);
33+
return computeHash(text);
34+
} else {
35+
var text = jsNodeToString(jsTree);
36+
new File(outputPath).writeAsStringSync(text);
37+
return computeHash(text);
38+
}
39+
}
40+
41+
void writeNodeToContext(JS.Node node, JS.JavaScriptPrintingContext context) {
42+
var opts = new JS.JavaScriptPrintingOptions(allowKeywordsInProperties: true);
43+
node.accept(new JS.Printer(opts, context, localNamer: new JSNamer(node)));
44+
}
45+
46+
String jsNodeToString(JS.Node node) {
47+
var context = new JS.SimpleJavaScriptPrintingContext();
48+
writeNodeToContext(node, context);
49+
return context.getText();
50+
}
51+
52+
class SourceMapPrintingContext extends JS.JavaScriptPrintingContext {
53+
final srcmaps.Printer printer;
54+
final String outputDir;
55+
56+
CompilationUnit unit;
57+
Uri uri;
58+
59+
SourceMapPrintingContext(this.printer, this.outputDir);
60+
61+
void emit(String string) {
62+
printer.add(string);
63+
}
64+
65+
void enterNode(JS.Node jsNode) {
66+
AstNode node = jsNode.sourceInformation;
67+
if (node is CompilationUnit) {
68+
unit = node;
69+
uri = _makeRelativeUri(unit.element.source.uri);
70+
return;
71+
}
72+
if (unit == null || node == null || node.offset == -1) return;
73+
74+
var loc = _location(node.offset);
75+
var name = _getIdentifier(node);
76+
if (name != null) {
77+
// TODO(jmesserly): mark only uses the beginning of the span, but
78+
// we're required to pass this as a valid span.
79+
var end = _location(node.end);
80+
printer.mark(new SourceMapSpan(loc, end, name, isIdentifier: true));
81+
} else {
82+
printer.mark(loc);
83+
}
84+
}
85+
86+
SourceLocation _location(int offset) => locationForOffset(unit, uri, offset);
87+
88+
Uri _makeRelativeUri(Uri src) {
89+
return new Uri(path: path.relative(src.path, from: outputDir));
90+
}
91+
92+
void exitNode(JS.Node jsNode) {
93+
AstNode node = jsNode.sourceInformation;
94+
if (node is CompilationUnit) {
95+
unit = null;
96+
uri = null;
97+
return;
98+
}
99+
if (unit == null || node == null || node.offset == -1) return;
100+
101+
// TODO(jmesserly): in many cases marking the end will be unnecessary.
102+
printer.mark(_location(node.end));
103+
}
104+
105+
String _getIdentifier(AstNode node) {
106+
if (node is SimpleIdentifier) return node.name;
107+
return null;
108+
}
109+
}

0 commit comments

Comments
 (0)