5
5
library dev_compiler.src.codegen.js_codegen;
6
6
7
7
import 'dart:collection' show HashSet, HashMap;
8
- import 'dart:io' show Directory, File;
9
8
10
9
import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
11
10
import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator;
@@ -14,9 +13,6 @@ import 'package:analyzer/src/generated/element.dart';
14
13
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
15
14
import 'package:analyzer/src/generated/scanner.dart'
16
15
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;
20
16
import 'package:path/path.dart' as path;
21
17
22
18
import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder;
@@ -31,8 +27,10 @@ import 'package:dev_compiler/src/options.dart';
31
27
import 'package:dev_compiler/src/utils.dart' ;
32
28
33
29
import 'code_generator.dart' ;
34
- import 'js_names.dart' ;
30
+ import 'js_names.dart' show JSTemporary, invalidJSStaticMethodName ;
35
31
import 'js_metalet.dart' ;
32
+ import 'js_printer.dart' show writeJsLibrary;
33
+ import 'side_effect_analysis.dart' ;
36
34
37
35
// Various dynamic helpers we call.
38
36
// If renaming these, make sure to check other places like the
@@ -1650,7 +1648,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
1650
1648
Map <String , JS .Expression > scope, String name, Expression expr,
1651
1649
{Expression context}) {
1652
1650
// No need to do anything for stateless expressions.
1653
- if (_isStateless (expr, context)) return expr;
1651
+ if (isStateless (expr, context)) return expr;
1654
1652
1655
1653
var t = _createTemporary ('#$name ' , expr.staticType);
1656
1654
scope[name] = _visit (expr);
@@ -2325,43 +2323,13 @@ class JSGenerator extends CodeGenerator {
2325
2323
TypeProvider get types => rules.provider;
2326
2324
2327
2325
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);
2351
2330
}
2352
2331
}
2353
2332
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
-
2365
2333
/// Choose a canonical name from the library element.
2366
2334
/// This never uses the library's name (the identifier in the `library`
2367
2335
/// declaration) as it doesn't have any meaningful rules enforced.
@@ -2391,143 +2359,6 @@ String jsOutputPath(LibraryInfo info, Uri root) {
2391
2359
return filepath;
2392
2360
}
2393
2361
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
-
2531
2362
// TODO(jmesserly): validate the library. See issue #135.
2532
2363
bool _isJsNameAnnotation (DartObjectImpl value) => value.type.name == 'JsName' ;
2533
2364
0 commit comments