Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Side by Side Diff: lib/src/codegen/js_codegen.dart

Issue 1062913004: [refactor] kick some stuff out of js_codegen.dart (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | lib/src/codegen/js_printer.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library dev_compiler.src.codegen.js_codegen; 5 library dev_compiler.src.codegen.js_codegen;
6 6
7 import 'dart:collection' show HashSet, HashMap; 7 import 'dart:collection' show HashSet, HashMap;
8 import 'dart:io' show Directory, File;
9 8
10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
11 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator;
12 import 'package:analyzer/src/generated/constant.dart'; 11 import 'package:analyzer/src/generated/constant.dart';
13 import 'package:analyzer/src/generated/element.dart'; 12 import 'package:analyzer/src/generated/element.dart';
14 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; 13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
15 import 'package:analyzer/src/generated/scanner.dart' 14 import 'package:analyzer/src/generated/scanner.dart'
16 show StringToken, Token, TokenType; 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 import 'package:path/path.dart' as path; 16 import 'package:path/path.dart' as path;
21 17
22 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; 18 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder;
23 19
24 // TODO(jmesserly): import from its own package 20 // TODO(jmesserly): import from its own package
25 import 'package:dev_compiler/src/js/js_ast.dart' as JS; 21 import 'package:dev_compiler/src/js/js_ast.dart' as JS;
26 import 'package:dev_compiler/src/js/js_ast.dart' show js; 22 import 'package:dev_compiler/src/js/js_ast.dart' show js;
27 23
28 import 'package:dev_compiler/src/checker/rules.dart'; 24 import 'package:dev_compiler/src/checker/rules.dart';
29 import 'package:dev_compiler/src/info.dart'; 25 import 'package:dev_compiler/src/info.dart';
30 import 'package:dev_compiler/src/options.dart'; 26 import 'package:dev_compiler/src/options.dart';
31 import 'package:dev_compiler/src/utils.dart'; 27 import 'package:dev_compiler/src/utils.dart';
32 28
33 import 'code_generator.dart'; 29 import 'code_generator.dart';
34 import 'js_names.dart'; 30 import 'js_names.dart' show JSTemporary, invalidJSStaticMethodName;
35 import 'js_metalet.dart'; 31 import 'js_metalet.dart';
32 import 'js_printer.dart' show writeJsLibrary;
33 import 'side_effect_analysis.dart';
36 34
37 // Various dynamic helpers we call. 35 // Various dynamic helpers we call.
38 // If renaming these, make sure to check other places like the 36 // If renaming these, make sure to check other places like the
39 // dart_runtime.js file and comments. 37 // dart_runtime.js file and comments.
40 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can 38 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can
41 // import and generate calls to, rather than dart_runtime.js 39 // import and generate calls to, rather than dart_runtime.js
42 const DPUT = 'dput'; 40 const DPUT = 'dput';
43 const DLOAD = 'dload'; 41 const DLOAD = 'dload';
44 const DINDEX = 'dindex'; 42 const DINDEX = 'dindex';
45 const DSETINDEX = 'dsetindex'; 43 const DSETINDEX = 'dsetindex';
(...skipping 1597 matching lines...) Expand 10 before | Expand all | Expand 10 after
1643 /// 1641 ///
1644 /// If the expression does not end up using `x` more than once, or if those 1642 /// If the expression does not end up using `x` more than once, or if those
1645 /// expressions can be treated as stateless (e.g. they are non-mutated 1643 /// expressions can be treated as stateless (e.g. they are non-mutated
1646 /// variables), then the resulting code will be simplified automatically. 1644 /// variables), then the resulting code will be simplified automatically.
1647 /// 1645 ///
1648 /// [scope] will be mutated to contain the new temporary's initialization. 1646 /// [scope] will be mutated to contain the new temporary's initialization.
1649 Expression _bindValue( 1647 Expression _bindValue(
1650 Map<String, JS.Expression> scope, String name, Expression expr, 1648 Map<String, JS.Expression> scope, String name, Expression expr,
1651 {Expression context}) { 1649 {Expression context}) {
1652 // No need to do anything for stateless expressions. 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 var t = _createTemporary('#$name', expr.staticType); 1653 var t = _createTemporary('#$name', expr.staticType);
1656 scope[name] = _visit(expr); 1654 scope[name] = _visit(expr);
1657 return t; 1655 return t;
1658 } 1656 }
1659 1657
1660 /// Desugars postfix increment. 1658 /// Desugars postfix increment.
1661 /// 1659 ///
1662 /// In the general case [expr] can be one of [IndexExpression], 1660 /// In the general case [expr] can be one of [IndexExpression],
1663 /// [PrefixExpression] or [PropertyAccess] and we need to 1661 /// [PrefixExpression] or [PropertyAccess] and we need to
(...skipping 654 matching lines...) Expand 10 before | Expand all | Expand 10 after
2318 ..addAll(e.accessors.map((m) => m.name)); 2316 ..addAll(e.accessors.map((m) => m.name));
2319 for (var name in names) { 2317 for (var name in names) {
2320 _extensionMethods.putIfAbsent(name, () => []).add(type); 2318 _extensionMethods.putIfAbsent(name, () => []).add(type);
2321 } 2319 }
2322 } 2320 }
2323 } 2321 }
2324 2322
2325 TypeProvider get types => rules.provider; 2323 TypeProvider get types => rules.provider;
2326 2324
2327 String generateLibrary(LibraryUnit unit, LibraryInfo info) { 2325 String generateLibrary(LibraryUnit unit, LibraryInfo info) {
2328 var jsTree = 2326 var codegen = new JSCodegenVisitor(info, rules, _extensionMethods);
2329 new JSCodegenVisitor(info, rules, _extensionMethods).emitLibrary(unit); 2327 var module = codegen.emitLibrary(unit);
2330 2328 var dir = path.join(outDir, jsOutputPath(info, root));
2331 var outputPath = path.join(outDir, jsOutputPath(info, root)); 2329 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps);
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 }
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 /// Choose a canonical name from the library element. 2333 /// Choose a canonical name from the library element.
2366 /// This never uses the library's name (the identifier in the `library` 2334 /// This never uses the library's name (the identifier in the `library`
2367 /// declaration) as it doesn't have any meaningful rules enforced. 2335 /// declaration) as it doesn't have any meaningful rules enforced.
2368 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); 2336 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library);
2369 2337
2370 /// Shorthand for identifier-like property names. 2338 /// Shorthand for identifier-like property names.
2371 /// For now, we emit them as strings and the printer restores them to 2339 /// For now, we emit them as strings and the printer restores them to
2372 /// identifiers if it can. 2340 /// identifiers if it can.
2373 // TODO(jmesserly): avoid the round tripping through quoted form. 2341 // TODO(jmesserly): avoid the round tripping through quoted form.
2374 JS.LiteralString _propertyName(String name) => js.string(name, "'"); 2342 JS.LiteralString _propertyName(String name) => js.string(name, "'");
2375 2343
2376 /// Path to file that will be generated for [info]. In case it's url is a 2344 /// Path to file that will be generated for [info]. In case it's url is a
2377 /// `file:` url, we use [root] to determine the relative path from the entry 2345 /// `file:` url, we use [root] to determine the relative path from the entry
2378 /// point file. 2346 /// point file.
2379 String jsOutputPath(LibraryInfo info, Uri root) { 2347 String jsOutputPath(LibraryInfo info, Uri root) {
2380 var uri = info.library.source.uri; 2348 var uri = info.library.source.uri;
2381 var filepath = '${path.withoutExtension(uri.path)}.js'; 2349 var filepath = '${path.withoutExtension(uri.path)}.js';
2382 if (uri.scheme == 'dart') { 2350 if (uri.scheme == 'dart') {
2383 filepath = 'dart/$filepath'; 2351 filepath = 'dart/$filepath';
2384 } else if (uri.scheme == 'file') { 2352 } else if (uri.scheme == 'file') {
2385 filepath = path.relative(filepath, from: path.dirname(root.path)); 2353 filepath = path.relative(filepath, from: path.dirname(root.path));
2386 } else { 2354 } else {
2387 assert(uri.scheme == 'package'); 2355 assert(uri.scheme == 'package');
2388 // filepath is good here, we want the output to start with a directory 2356 // filepath is good here, we want the output to start with a directory
2389 // matching the package name. 2357 // matching the package name.
2390 } 2358 }
2391 return filepath; 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 // TODO(jmesserly): validate the library. See issue #135. 2362 // TODO(jmesserly): validate the library. See issue #135.
2532 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; 2363 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName';
2533 2364
2534 // TODO(jacobr): we would like to do something like the following 2365 // TODO(jacobr): we would like to do something like the following
2535 // but we don't have summary support yet. 2366 // but we don't have summary support yet.
2536 // bool _supportJsExtensionMethod(AnnotatedNode node) => 2367 // bool _supportJsExtensionMethod(AnnotatedNode node) =>
2537 // _getAnnotation(node, "SupportJsExtensionMethod") != null; 2368 // _getAnnotation(node, "SupportJsExtensionMethod") != null;
OLDNEW
« no previous file with comments | « no previous file | lib/src/codegen/js_printer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698