OLD | NEW |
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; | 8 import 'dart:io' show Directory, File; |
9 | 9 |
10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
(...skipping 11 matching lines...) Expand all Loading... |
22 | 22 |
23 // TODO(jmesserly): import from its own package | 23 // TODO(jmesserly): import from its own package |
24 import 'package:dev_compiler/src/js/js_ast.dart' as JS; | 24 import 'package:dev_compiler/src/js/js_ast.dart' as JS; |
25 import 'package:dev_compiler/src/js/js_ast.dart' show js; | 25 import 'package:dev_compiler/src/js/js_ast.dart' show js; |
26 | 26 |
27 import 'package:dev_compiler/src/checker/rules.dart'; | 27 import 'package:dev_compiler/src/checker/rules.dart'; |
28 import 'package:dev_compiler/src/info.dart'; | 28 import 'package:dev_compiler/src/info.dart'; |
29 import 'package:dev_compiler/src/options.dart'; | 29 import 'package:dev_compiler/src/options.dart'; |
30 import 'package:dev_compiler/src/report.dart'; | 30 import 'package:dev_compiler/src/report.dart'; |
31 import 'package:dev_compiler/src/utils.dart'; | 31 import 'package:dev_compiler/src/utils.dart'; |
| 32 |
32 import 'code_generator.dart'; | 33 import 'code_generator.dart'; |
| 34 import 'js_names.dart'; |
33 | 35 |
34 bool _isAnnotationType(Annotation m, String name) => m.name.name == name; | 36 bool _isAnnotationType(Annotation m, String name) => m.name.name == name; |
35 | 37 |
36 Annotation _getAnnotation(AnnotatedNode node, String name) => node.metadata | 38 Annotation _getAnnotation(AnnotatedNode node, String name) => node.metadata |
37 .firstWhere((annotation) => _isAnnotationType(annotation, name), | 39 .firstWhere((annotation) => _isAnnotationType(annotation, name), |
38 orElse: () => null); | 40 orElse: () => null); |
39 | 41 |
40 Annotation _getJsNameAnnotation(AnnotatedNode node) => | 42 Annotation _getJsNameAnnotation(AnnotatedNode node) => |
41 _getAnnotation(node, "JsName"); | 43 _getAnnotation(node, "JsName"); |
42 | 44 |
43 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { | 45 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
44 final LibraryInfo libraryInfo; | 46 final LibraryInfo libraryInfo; |
45 final TypeRules rules; | 47 final TypeRules rules; |
46 | 48 |
47 // TODO(jmesserly): this is needed because RestrictedTypeRules can send | 49 // TODO(jmesserly): this is needed because RestrictedTypeRules can send |
48 // messages to CheckerReporter, for things like missing types. | 50 // messages to CheckerReporter, for things like missing types. |
49 // We should probably refactor so this can't happen, as codegen would be too | 51 // We should probably refactor so this can't happen, as codegen would be too |
50 // late to be issuing these messages. | 52 // late to be issuing these messages. |
51 final CheckerReporter _checkerReporter; | 53 final CheckerReporter _checkerReporter; |
52 | 54 |
53 /// The variable for the target of the current `..` cascade expression. | 55 /// The variable for the target of the current `..` cascade expression. |
54 SimpleIdentifier _cascadeTarget; | 56 SimpleIdentifier _cascadeTarget; |
55 /// The variable for the current catch clause | 57 /// The variable for the current catch clause |
56 String _catchParameter; | 58 SimpleIdentifier _catchParameter; |
57 | 59 |
58 ClassDeclaration currentClass; | 60 ClassDeclaration currentClass; |
59 ConstantEvaluator _constEvaluator; | 61 ConstantEvaluator _constEvaluator; |
60 | 62 |
61 final _exports = <String>[]; | 63 final _exports = <String>[]; |
62 final _lazyFields = <VariableDeclaration>[]; | 64 final _lazyFields = <VariableDeclaration>[]; |
63 final _properties = <FunctionDeclaration>[]; | 65 final _properties = <FunctionDeclaration>[]; |
64 final _privateNames = new HashSet<String>(); | 66 final _privateNames = new HashSet<String>(); |
65 final _pendingPrivateNames = <String>[]; | 67 final _pendingPrivateNames = <String>[]; |
66 | 68 |
67 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or | 69 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or |
68 /// [ClassTypeAlias]. | 70 /// [ClassTypeAlias]. |
69 final _pendingClasses = new HashMap<ClassElement, CompilationUnitMember>(); | 71 final _pendingClasses = new HashMap<ClassElement, CompilationUnitMember>(); |
70 | 72 |
71 /// Memoized results of [_lazyClass]. | 73 /// Memoized results of [_lazyClass]. |
72 final _lazyClassMemo = new HashMap<ClassElement, bool>(); | 74 final _lazyClassMemo = new HashMap<ClassElement, bool>(); |
73 | 75 |
74 /// Memoized results of [_inLibraryCycle]. | 76 /// Memoized results of [_inLibraryCycle]. |
75 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); | 77 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); |
76 | 78 |
77 JSCodegenVisitor(this.libraryInfo, this.rules, this._checkerReporter); | 79 JSCodegenVisitor(this.libraryInfo, this.rules, this._checkerReporter); |
78 | 80 |
79 LibraryElement get currentLibrary => libraryInfo.library; | 81 LibraryElement get currentLibrary => libraryInfo.library; |
80 | 82 |
| 83 /// The name for the library's exports inside itself. |
| 84 /// This much be a constant because we interpolate it into template strings, |
| 85 /// and otherwise it would break caching for them. |
| 86 /// `exports` was chosen as the most similar to ES module patterns. |
| 87 final JSTemporary _exportsVar = new JSTemporary('exports'); |
| 88 final JSTemporary _namedArgTemp = new JSTemporary('opts'); |
| 89 |
81 JS.Program emitLibrary(LibraryUnit library) { | 90 JS.Program emitLibrary(LibraryUnit library) { |
82 var jsDefaultValue = '{}'; | 91 var jsDefaultValue = '{}'; |
83 var unit = library.library; | 92 var unit = library.library; |
84 if (unit.directives.isNotEmpty) { | 93 if (unit.directives.isNotEmpty) { |
85 var annotation = _getJsNameAnnotation(unit.directives.first); | 94 var annotation = _getJsNameAnnotation(unit.directives.first); |
86 if (annotation != null) { | 95 if (annotation != null) { |
87 var arguments = annotation.arguments.arguments; | 96 var arguments = annotation.arguments.arguments; |
88 if (!arguments.isEmpty) { | 97 if (!arguments.isEmpty) { |
89 var namedExpression = arguments.first as NamedExpression; | 98 var namedExpression = arguments.first as NamedExpression; |
90 var literal = namedExpression.expression as SimpleStringLiteral; | 99 var literal = namedExpression.expression as SimpleStringLiteral; |
(...skipping 15 matching lines...) Expand all Loading... |
106 } | 115 } |
107 | 116 |
108 for (var unit in library.partsThenLibrary) body.add(_visit(unit)); | 117 for (var unit in library.partsThenLibrary) body.add(_visit(unit)); |
109 | 118 |
110 assert(_pendingClasses.isEmpty); | 119 assert(_pendingClasses.isEmpty); |
111 | 120 |
112 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); | 121 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); |
113 | 122 |
114 // TODO(jmesserly): make these immutable in JS? | 123 // TODO(jmesserly): make these immutable in JS? |
115 for (var name in _exports) { | 124 for (var name in _exports) { |
116 body.add(js.statement('$_EXPORTS.# = #;', [name, name])); | 125 body.add(js.statement('#.# = #;', [_exportsVar, name, name])); |
117 } | 126 } |
118 | 127 |
119 var name = jsLibraryName(libraryInfo.library); | 128 var name = jsLibraryName(libraryInfo.library); |
120 | 129 |
121 var defaultValue = js.call(jsDefaultValue); | 130 var defaultValue = js.call(jsDefaultValue); |
122 return new JS.Program([ | 131 return new JS.Program([ |
123 js.statement('var #;', name), | 132 js.statement('var #;', name), |
124 js.statement("(function($_EXPORTS) { 'use strict'; #; })(# || (# = #));", | 133 js.statement("(function(#) { 'use strict'; #; })(# || (# = #));", [ |
125 [body, name, name, defaultValue]) | 134 _exportsVar, |
| 135 body, |
| 136 name, |
| 137 name, |
| 138 defaultValue |
| 139 ]) |
126 ]); | 140 ]); |
127 } | 141 } |
128 | 142 |
129 JS.Statement _initPrivateSymbol(String name) => | 143 JS.Statement _initPrivateSymbol(String name) => js.statement( |
130 js.statement('let # = $_SYMBOL(#);', [name, js.string(name, "'")]); | 144 'let # = $_SYMBOL(#);', [new JSTemporary(name), js.string(name, "'")]); |
131 | 145 |
132 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, | 146 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, |
133 // until we have better name tracking. | 147 // until we have better name tracking. |
134 String get _SYMBOL => currentLibrary.isDartCore ? 'dart.JsSymbol' : 'Symbol'; | 148 String get _SYMBOL => currentLibrary.isDartCore ? 'dart.JsSymbol' : 'Symbol'; |
135 | 149 |
136 @override | 150 @override |
137 JS.Statement visitCompilationUnit(CompilationUnit node) { | 151 JS.Statement visitCompilationUnit(CompilationUnit node) { |
138 var source = node.element.source; | 152 var source = node.element.source; |
139 | 153 |
140 _constEvaluator = new ConstantEvaluator(source, rules.provider); | 154 _constEvaluator = new ConstantEvaluator(source, rules.provider); |
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
308 | 322 |
309 JS.Statement genericDef; | 323 JS.Statement genericDef; |
310 JS.Expression genericInst; | 324 JS.Expression genericInst; |
311 if (classElem.typeParameters.isNotEmpty) { | 325 if (classElem.typeParameters.isNotEmpty) { |
312 genericDef = _emitGenericClassDef(classElem, body); | 326 genericDef = _emitGenericClassDef(classElem, body); |
313 var dynamicArgs = new List.filled( | 327 var dynamicArgs = new List.filled( |
314 classElem.typeParameters.length, js.call('dart.dynamic')); | 328 classElem.typeParameters.length, js.call('dart.dynamic')); |
315 | 329 |
316 var target = genericName; | 330 var target = genericName; |
317 if (_needQualifiedName(classElem)) { | 331 if (_needQualifiedName(classElem)) { |
318 target = js.call('#.#', [_EXPORTS, genericName]); | 332 target = js.call('#.#', [_exportsVar, genericName]); |
319 } | 333 } |
320 genericInst = js.call('#(#)', [target, dynamicArgs]); | 334 genericInst = js.call('#(#)', [target, dynamicArgs]); |
321 } | 335 } |
322 | 336 |
323 // The base class and all mixins must be declared before this class. | 337 // The base class and all mixins must be declared before this class. |
324 if (_lazyClass(classElem)) { | 338 if (_lazyClass(classElem)) { |
325 // TODO(jmesserly): the lazy class def is a simple solution for now. | 339 // TODO(jmesserly): the lazy class def is a simple solution for now. |
326 // We may want to consider other options in the future. | 340 // We may want to consider other options in the future. |
327 | 341 |
328 if (genericDef != null) { | 342 if (genericDef != null) { |
329 return js.statement( | 343 return js.statement( |
330 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', [ | 344 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', [ |
331 genericDef, | 345 genericDef, |
332 _EXPORTS, | 346 _exportsVar, |
333 js.string(name, "'"), | 347 js.string(name, "'"), |
334 genericName | 348 genericName |
335 ]); | 349 ]); |
336 } | 350 } |
337 | 351 |
338 return js.statement( | 352 return js.statement( |
339 'dart.defineLazyClass(#, { get #() { #; return #; } });', [ | 353 'dart.defineLazyClass(#, { get #() { #; return #; } });', [ |
340 _EXPORTS, | 354 _exportsVar, |
341 _propertyName(name), | 355 _propertyName(name), |
342 body, | 356 body, |
343 name | 357 name |
344 ]); | 358 ]); |
345 } | 359 } |
346 | 360 |
347 if (isPublic(name)) _exports.add(name); | 361 if (isPublic(name)) _exports.add(name); |
348 | 362 |
349 if (genericDef != null) { | 363 if (genericDef != null) { |
350 body = js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); | 364 body = js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); |
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
554 var fieldName = field.name.name; | 568 var fieldName = field.name.name; |
555 if (field.isConst || _isFieldInitConstant(field)) { | 569 if (field.isConst || _isFieldInitConstant(field)) { |
556 var init = _visit(field.initializer); | 570 var init = _visit(field.initializer); |
557 if (init == null) init = new JS.LiteralNull(); | 571 if (init == null) init = new JS.LiteralNull(); |
558 body.add(js.statement('#.# = #;', [name, fieldName, init])); | 572 body.add(js.statement('#.# = #;', [name, fieldName, init])); |
559 } else { | 573 } else { |
560 lazyStatics.add(field); | 574 lazyStatics.add(field); |
561 } | 575 } |
562 } | 576 } |
563 } | 577 } |
564 var lazy = _emitLazyFields(name, lazyStatics); | 578 var lazy = _emitLazyFields(new JS.Identifier(name), lazyStatics); |
565 if (lazy != null) body.add(lazy); | 579 if (lazy != null) body.add(lazy); |
566 return _statement(body); | 580 return _statement(body); |
567 } | 581 } |
568 | 582 |
569 /// Generates the implicit default constructor for class C of the form | 583 /// Generates the implicit default constructor for class C of the form |
570 /// `C() : super() {}`. | 584 /// `C() : super() {}`. |
571 JS.Method _emitImplicitConstructor( | 585 JS.Method _emitImplicitConstructor( |
572 ClassDeclaration node, String name, List<FieldDeclaration> fields) { | 586 ClassDeclaration node, String name, List<FieldDeclaration> fields) { |
573 // If we don't have a method body, skip this. | 587 // If we don't have a method body, skip this. |
574 if (fields.isEmpty) return null; | 588 if (fields.isEmpty) return null; |
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
794 if (parameters == null || !_hasArgumentInitializers(parameters)) { | 808 if (parameters == null || !_hasArgumentInitializers(parameters)) { |
795 return null; | 809 return null; |
796 } | 810 } |
797 | 811 |
798 var body = []; | 812 var body = []; |
799 for (var param in parameters.parameters) { | 813 for (var param in parameters.parameters) { |
800 // TODO(justinfagnani): rename identifier if necessary | 814 // TODO(justinfagnani): rename identifier if necessary |
801 var name = param.identifier.name; | 815 var name = param.identifier.name; |
802 | 816 |
803 if (param.kind == ParameterKind.NAMED) { | 817 if (param.kind == ParameterKind.NAMED) { |
804 body.add(js.statement('let # = opt\$ && # in opt\$ ? opt\$.# : #;', [ | 818 body.add(js.statement('let # = # && # in # ? #.# : #;', [ |
805 name, | 819 name, |
| 820 _namedArgTemp, |
806 js.string(name, "'"), | 821 js.string(name, "'"), |
| 822 _namedArgTemp, |
| 823 _namedArgTemp, |
807 name, | 824 name, |
808 _defaultParamValue(param), | 825 _defaultParamValue(param), |
809 ])); | 826 ])); |
810 } else if (param.kind == ParameterKind.POSITIONAL) { | 827 } else if (param.kind == ParameterKind.POSITIONAL) { |
811 body.add(js.statement('if (# === void 0) # = #;', [ | 828 body.add(js.statement('if (# === void 0) # = #;', [ |
812 name, | 829 name, |
813 name, | 830 name, |
814 _defaultParamValue(param) | 831 _defaultParamValue(param) |
815 ])); | 832 ])); |
816 } | 833 } |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
927 | 944 |
928 if (e.enclosingElement is CompilationUnitElement && | 945 if (e.enclosingElement is CompilationUnitElement && |
929 (e.library != libraryInfo.library || | 946 (e.library != libraryInfo.library || |
930 variable is TopLevelVariableElement && !variable.isConst)) { | 947 variable is TopLevelVariableElement && !variable.isConst)) { |
931 return js.call('#.#', [_libraryName(e.library), name]); | 948 return js.call('#.#', [_libraryName(e.library), name]); |
932 } else if (currentClass != null && _needsImplicitThis(e)) { | 949 } else if (currentClass != null && _needsImplicitThis(e)) { |
933 return js.call('this.#', _jsMemberName(name)); | 950 return js.call('this.#', _jsMemberName(name)); |
934 } else if (variable is ConstFieldElementImpl) { | 951 } else if (variable is ConstFieldElementImpl) { |
935 var className = (variable.enclosingElement as ClassElement).name; | 952 var className = (variable.enclosingElement as ClassElement).name; |
936 return js.call('#.#', [className, name]); | 953 return js.call('#.#', [className, name]); |
937 } else if (e is ParameterElement && e.isInitializingFormal) { | 954 } else if (e is ParameterElement && e.isInitializingFormal && e.isPrivate) { |
938 name = _fieldParameterName(name); | 955 /// Rename private names so they don't shadow the private field symbol. |
| 956 /// The renamer would handle this, but it would prefer to rename the |
| 957 /// temporary used for the private symbol. Instead rename the parameter. |
| 958 return new JSTemporary('${name.substring(1)}'); |
| 959 } else if (_isTemporary(e)) { |
| 960 return new JSTemporary(e.name); |
939 } | 961 } |
940 return new JS.Identifier(name); | 962 return new JS.Identifier(name); |
941 } | 963 } |
942 | 964 |
943 JS.Expression _emitTypeName(DartType type) { | 965 JS.Expression _emitTypeName(DartType type) { |
944 var name = type.name; | 966 var name = type.name; |
945 var element = type.element; | 967 var element = type.element; |
946 if (name == '') { | 968 if (name == '') { |
947 // TODO(jmesserly): remove when we're using coercion reifier. | 969 // TODO(jmesserly): remove when we're using coercion reifier. |
948 return _unimplementedCall('Unimplemented type $type'); | 970 return _unimplementedCall('Unimplemented type $type'); |
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1155 assert(node.parent is ArgumentList); | 1177 assert(node.parent is ArgumentList); |
1156 return new JS.Property( | 1178 return new JS.Property( |
1157 _propertyName(node.name.label.name), _visit(node.expression)); | 1179 _propertyName(node.name.label.name), _visit(node.expression)); |
1158 } | 1180 } |
1159 | 1181 |
1160 @override | 1182 @override |
1161 List<JS.Identifier> visitFormalParameterList(FormalParameterList node) { | 1183 List<JS.Identifier> visitFormalParameterList(FormalParameterList node) { |
1162 var result = <JS.Identifier>[]; | 1184 var result = <JS.Identifier>[]; |
1163 for (FormalParameter param in node.parameters) { | 1185 for (FormalParameter param in node.parameters) { |
1164 if (param.kind == ParameterKind.NAMED) { | 1186 if (param.kind == ParameterKind.NAMED) { |
1165 result.add(new JS.Identifier(r'opt$')); | 1187 result.add(_namedArgTemp); |
1166 break; | 1188 break; |
1167 } | 1189 } |
1168 result.add(_visit(param)); | 1190 result.add(_visit(param)); |
1169 } | 1191 } |
1170 return result; | 1192 return result; |
1171 } | 1193 } |
1172 | 1194 |
1173 @override | 1195 @override |
1174 JS.Statement visitExpressionStatement(ExpressionStatement node) => | 1196 JS.Statement visitExpressionStatement(ExpressionStatement node) => |
1175 _expressionStatement(_visit(node.expression)); | 1197 _expressionStatement(_visit(node.expression)); |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1258 } | 1280 } |
1259 | 1281 |
1260 JS.Expression _visitInitializer(VariableDeclaration node) { | 1282 JS.Expression _visitInitializer(VariableDeclaration node) { |
1261 var value = _visit(node.initializer); | 1283 var value = _visit(node.initializer); |
1262 // explicitly initialize to null, to avoid getting `undefined`. | 1284 // explicitly initialize to null, to avoid getting `undefined`. |
1263 // TODO(jmesserly): do this only for vars that aren't definitely assigned. | 1285 // TODO(jmesserly): do this only for vars that aren't definitely assigned. |
1264 return value != null ? value : new JS.LiteralNull(); | 1286 return value != null ? value : new JS.LiteralNull(); |
1265 } | 1287 } |
1266 | 1288 |
1267 void _flushLazyFields(List<JS.Statement> body) { | 1289 void _flushLazyFields(List<JS.Statement> body) { |
1268 var code = _emitLazyFields(_EXPORTS, _lazyFields); | 1290 var code = _emitLazyFields(_exportsVar, _lazyFields); |
1269 if (code != null) body.add(code); | 1291 if (code != null) body.add(code); |
1270 _lazyFields.clear(); | 1292 _lazyFields.clear(); |
1271 } | 1293 } |
1272 | 1294 |
1273 JS.Statement _emitLazyFields( | 1295 JS.Statement _emitLazyFields( |
1274 String objExpr, List<VariableDeclaration> fields) { | 1296 JS.Expression objExpr, List<VariableDeclaration> fields) { |
1275 if (fields.isEmpty) return null; | 1297 if (fields.isEmpty) return null; |
1276 | 1298 |
1277 var methods = []; | 1299 var methods = []; |
1278 for (var node in fields) { | 1300 for (var node in fields) { |
1279 var name = node.name.name; | 1301 var name = node.name.name; |
1280 methods.add(new JS.Method(_propertyName(name), | 1302 methods.add(new JS.Method(_propertyName(name), |
1281 js.call('function() { return #; }', _visit(node.initializer)), | 1303 js.call('function() { return #; }', _visit(node.initializer)), |
1282 isGetter: true)); | 1304 isGetter: true)); |
1283 | 1305 |
1284 // TODO(jmesserly): use a dummy setter to indicate writable. | 1306 // TODO(jmesserly): use a dummy setter to indicate writable. |
1285 if (!node.isFinal) { | 1307 if (!node.isFinal) { |
1286 methods.add(new JS.Method( | 1308 methods.add(new JS.Method( |
1287 _propertyName(name), js.call('function(_) {}'), isSetter: true)); | 1309 _propertyName(name), js.call('function(_) {}'), isSetter: true)); |
1288 } | 1310 } |
1289 } | 1311 } |
1290 | 1312 |
1291 return js.statement( | 1313 return js.statement( |
1292 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); | 1314 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); |
1293 } | 1315 } |
1294 | 1316 |
1295 void _flushLibraryProperties(List<JS.Statement> body) { | 1317 void _flushLibraryProperties(List<JS.Statement> body) { |
1296 if (_properties.isEmpty) return; | 1318 if (_properties.isEmpty) return; |
1297 body.add(js.statement('dart.copyProperties($_EXPORTS, { # });', | 1319 body.add(js.statement('dart.copyProperties(#, { # });', [ |
1298 [_properties.map(_emitTopLevelProperty)])); | 1320 _exportsVar, |
| 1321 _properties.map(_emitTopLevelProperty) |
| 1322 ])); |
1299 _properties.clear(); | 1323 _properties.clear(); |
1300 } | 1324 } |
1301 | 1325 |
1302 @override | 1326 @override |
1303 JS.Statement visitVariableDeclarationStatement( | 1327 JS.Statement visitVariableDeclarationStatement( |
1304 VariableDeclarationStatement node) => | 1328 VariableDeclarationStatement node) => |
1305 _expressionStatement(_visit(node.variables)); | 1329 _expressionStatement(_visit(node.variables)); |
1306 | 1330 |
1307 @override | 1331 @override |
1308 visitConstructorName(ConstructorName node) { | 1332 visitConstructorName(ConstructorName node) { |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1459 // Generic static-dispatch, user-defined operator code path. | 1483 // Generic static-dispatch, user-defined operator code path. |
1460 return js.call('#.#(#)', [_visit(left), opString, _visit(right)]); | 1484 return js.call('#.#(#)', [_visit(left), opString, _visit(right)]); |
1461 } | 1485 } |
1462 } | 1486 } |
1463 } | 1487 } |
1464 | 1488 |
1465 bool _isNull(Expression expr) => expr is NullLiteral; | 1489 bool _isNull(Expression expr) => expr is NullLiteral; |
1466 | 1490 |
1467 // TODO(jmesserly, vsm): Refactor this logic. | 1491 // TODO(jmesserly, vsm): Refactor this logic. |
1468 SimpleIdentifier _createTemporary(String name, DartType type) { | 1492 SimpleIdentifier _createTemporary(String name, DartType type) { |
| 1493 // We use an invalid source location to signal that this is a temporary. |
| 1494 // See [_isTemporary]. |
| 1495 // TODO(jmesserly): alternatives are |
| 1496 // * (ab)use Element.isSynthetic, which isn't currently used for |
| 1497 // LocalVariableElementImpl, so we could repurpose to mean "temp". |
| 1498 // * add a new property to LocalVariableElementImpl. |
| 1499 // * create a new subtype of LocalVariableElementImpl to mark a temp. |
1469 var id = | 1500 var id = |
1470 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, 0)); | 1501 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); |
1471 id.staticElement = new LocalVariableElementImpl.forNode(id); | 1502 id.staticElement = new LocalVariableElementImpl.forNode(id); |
1472 id.staticType = type; | 1503 id.staticType = type; |
1473 return id; | 1504 return id; |
1474 } | 1505 } |
1475 | 1506 |
| 1507 bool _isTemporary(Element node) => node.nameOffset == -1; |
| 1508 |
1476 JS.Expression _emitPostfixIncrement(Expression expr, Token op) { | 1509 JS.Expression _emitPostfixIncrement(Expression expr, Token op) { |
1477 var type = rules.getStaticType(expr); | 1510 var type = rules.getStaticType(expr); |
1478 assert(type != null); | 1511 assert(type != null); |
1479 var tmp = _createTemporary('\$tmp', type); | 1512 var tmp = _createTemporary('x', type); |
1480 | 1513 |
1481 // Increment and write | 1514 // Increment and write |
1482 var one = AstBuilder.integerLiteral(1); | 1515 var one = AstBuilder.integerLiteral(1); |
1483 one.staticType = rules.provider.intType; | 1516 one.staticType = rules.provider.intType; |
1484 var increment = AstBuilder.binaryExpression(tmp, op.lexeme[0], one); | 1517 var increment = AstBuilder.binaryExpression(tmp, op.lexeme[0], one); |
1485 increment.staticType = type; | 1518 increment.staticType = type; |
1486 var write = _emitAssignment(expr, increment); | 1519 var write = _emitAssignment(expr, increment); |
1487 | 1520 |
1488 var bindThis = _maybeBindThis(expr); | 1521 var bindThis = _maybeBindThis(expr); |
1489 return js.call("((#) => (#, #))$bindThis(#)", [ | 1522 return js.call("((#) => (#, #))$bindThis(#)", [ |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1581 // (sb.write(1), sb.write(2), sb) | 1614 // (sb.write(1), sb.write(2), sb) |
1582 var sections = _visitListToBinary(node.cascadeSections, ','); | 1615 var sections = _visitListToBinary(node.cascadeSections, ','); |
1583 result = new JS.Binary(',', sections, _visit(_cascadeTarget)); | 1616 result = new JS.Binary(',', sections, _visit(_cascadeTarget)); |
1584 } | 1617 } |
1585 } else { | 1618 } else { |
1586 // In the general case we need to capture the target expression into | 1619 // In the general case we need to capture the target expression into |
1587 // a temporary. This uses a lambda to get a temporary scope, and it also | 1620 // a temporary. This uses a lambda to get a temporary scope, and it also |
1588 // remains valid in an expression context. | 1621 // remains valid in an expression context. |
1589 // TODO(jmesserly): need a better way to handle temps. | 1622 // TODO(jmesserly): need a better way to handle temps. |
1590 // TODO(jmesserly): special case for parent is ExpressionStatement? | 1623 // TODO(jmesserly): special case for parent is ExpressionStatement? |
1591 _cascadeTarget = | 1624 _cascadeTarget = _createTemporary('_', node.target.staticType); |
1592 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, '_', 0)); | |
1593 _cascadeTarget.staticElement = | |
1594 new LocalVariableElementImpl.forNode(_cascadeTarget); | |
1595 _cascadeTarget.staticType = node.target.staticType; | |
1596 | 1625 |
1597 var body = _visitList(node.cascadeSections); | 1626 var body = _visitList(node.cascadeSections); |
1598 if (node.parent is! ExpressionStatement) { | 1627 if (node.parent is! ExpressionStatement) { |
1599 body.add(js.statement('return #;', _cascadeTarget.name)); | 1628 body.add(js.statement('return #;', _visit(_cascadeTarget))); |
1600 } | 1629 } |
1601 | 1630 |
1602 var bindThis = _maybeBindThis(node.cascadeSections); | 1631 var bindThis = _maybeBindThis(node.cascadeSections); |
1603 result = js.call('((#) => { # })$bindThis(#)', [ | 1632 result = js.call('((#) => { # })$bindThis(#)', [ |
1604 _cascadeTarget.name, | 1633 _visit(_cascadeTarget), |
1605 body, | 1634 body, |
1606 _visit(node.target) | 1635 _visit(node.target) |
1607 ]); | 1636 ]); |
1608 } | 1637 } |
1609 | 1638 |
1610 _cascadeTarget = savedCascadeTemp; | 1639 _cascadeTarget = savedCascadeTemp; |
1611 return result; | 1640 return result; |
1612 } | 1641 } |
1613 | 1642 |
1614 /// True is the expression can be evaluated multiple times without causing | 1643 /// True is the expression can be evaluated multiple times without causing |
(...skipping 16 matching lines...) Expand all Loading... |
1631 return false; | 1660 return false; |
1632 } | 1661 } |
1633 | 1662 |
1634 @override | 1663 @override |
1635 visitParenthesizedExpression(ParenthesizedExpression node) => | 1664 visitParenthesizedExpression(ParenthesizedExpression node) => |
1636 // The printer handles precedence so we don't need to. | 1665 // The printer handles precedence so we don't need to. |
1637 _visit(node.expression); | 1666 _visit(node.expression); |
1638 | 1667 |
1639 @override | 1668 @override |
1640 visitFormalParameter(FormalParameter node) => | 1669 visitFormalParameter(FormalParameter node) => |
1641 new JS.Identifier(node.identifier.name); | 1670 visitSimpleIdentifier(node.identifier); |
1642 | |
1643 @override | |
1644 visitFieldFormalParameter(FieldFormalParameter node) => | |
1645 new JS.Identifier(_fieldParameterName(node.identifier.name)); | |
1646 | |
1647 /// Rename private names so they don't shadow the private field symbol. | |
1648 // TODO(jmesserly): replace this ad-hoc rename with a general strategy. | |
1649 _fieldParameterName(name) => name.startsWith('_') ? '\$$name' : name; | |
1650 | 1671 |
1651 @override | 1672 @override |
1652 JS.This visitThisExpression(ThisExpression node) => new JS.This(); | 1673 JS.This visitThisExpression(ThisExpression node) => new JS.This(); |
1653 | 1674 |
1654 @override | 1675 @override |
1655 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); | 1676 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); |
1656 | 1677 |
1657 @override | 1678 @override |
1658 visitPrefixedIdentifier(PrefixedIdentifier node) { | 1679 visitPrefixedIdentifier(PrefixedIdentifier node) { |
1659 if (node.prefix.staticElement is PrefixElement) { | 1680 if (node.prefix.staticElement is PrefixElement) { |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1712 if (node.parent is ExpressionStatement) { | 1733 if (node.parent is ExpressionStatement) { |
1713 return js.statement('throw #;', expr); | 1734 return js.statement('throw #;', expr); |
1714 } else { | 1735 } else { |
1715 return js.call('dart.throw_(#)', expr); | 1736 return js.call('dart.throw_(#)', expr); |
1716 } | 1737 } |
1717 } | 1738 } |
1718 | 1739 |
1719 @override | 1740 @override |
1720 visitRethrowExpression(RethrowExpression node) { | 1741 visitRethrowExpression(RethrowExpression node) { |
1721 if (node.parent is ExpressionStatement) { | 1742 if (node.parent is ExpressionStatement) { |
1722 return js.statement('throw #;', _catchParameter); | 1743 return js.statement('throw #;', _visit(_catchParameter)); |
1723 } else { | 1744 } else { |
1724 return js.call('dart.throw_(#)', _catchParameter); | 1745 return js.call('dart.throw_(#)', _visit(_catchParameter)); |
1725 } | 1746 } |
1726 } | 1747 } |
1727 | 1748 |
1728 @override | 1749 @override |
1729 JS.If visitIfStatement(IfStatement node) { | 1750 JS.If visitIfStatement(IfStatement node) { |
1730 return new JS.If(_visit(node.condition), _visit(node.thenStatement), | 1751 return new JS.If(_visit(node.condition), _visit(node.thenStatement), |
1731 _visitOrEmpty(node.elseStatement)); | 1752 _visitOrEmpty(node.elseStatement)); |
1732 } | 1753 } |
1733 | 1754 |
1734 @override | 1755 @override |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1775 return new JS.Try(_visit(node.body), _visitCatch(node.catchClauses), | 1796 return new JS.Try(_visit(node.body), _visitCatch(node.catchClauses), |
1776 _visit(node.finallyBlock)); | 1797 _visit(node.finallyBlock)); |
1777 } | 1798 } |
1778 | 1799 |
1779 _visitCatch(NodeList<CatchClause> clauses) { | 1800 _visitCatch(NodeList<CatchClause> clauses) { |
1780 if (clauses == null || clauses.isEmpty) return null; | 1801 if (clauses == null || clauses.isEmpty) return null; |
1781 | 1802 |
1782 // TODO(jmesserly): need a better way to get a temporary variable. | 1803 // TODO(jmesserly): need a better way to get a temporary variable. |
1783 // This could incorrectly shadow a user's name. | 1804 // This could incorrectly shadow a user's name. |
1784 var savedCatch = _catchParameter; | 1805 var savedCatch = _catchParameter; |
1785 _catchParameter = '\$e'; | |
1786 | 1806 |
1787 if (clauses.length == 1) { | 1807 if (clauses.length == 1) { |
1788 // Special case for a single catch. | 1808 // Special case for a single catch. |
1789 var clause = clauses.single; | 1809 _catchParameter = clauses.single.exceptionParameter; |
1790 if (clause.exceptionParameter != null) { | 1810 } else { |
1791 _catchParameter = clause.exceptionParameter.name; | 1811 _catchParameter = _createTemporary('e', rules.provider.dynamicType); |
1792 } | |
1793 } | 1812 } |
1794 | 1813 |
1795 var catchVarDecl = new JS.Identifier(_catchParameter); | 1814 JS.Statement catchBody = null; |
1796 var catchBody = new JS.Block(_visitList(clauses)); | 1815 for (var clause in clauses.reversed) { |
| 1816 catchBody = _catchClauseGuard(clause, catchBody); |
| 1817 } |
| 1818 |
| 1819 var catchVarDecl = _visit(_catchParameter); |
1797 _catchParameter = savedCatch; | 1820 _catchParameter = savedCatch; |
| 1821 return new JS.Catch(catchVarDecl, new JS.Block([catchBody])); |
| 1822 } |
1798 | 1823 |
1799 return new JS.Catch(catchVarDecl, catchBody); | 1824 JS.Statement _catchClauseGuard(CatchClause clause, JS.Statement otherwise) { |
| 1825 var then = visitCatchClause(clause); |
| 1826 |
| 1827 // Discard following clauses, if any, as they are unreachable. |
| 1828 if (clause.exceptionType == null) return then; |
| 1829 |
| 1830 return new JS.If(js.call('dart.is(#, #)', [ |
| 1831 _visit(_catchParameter), |
| 1832 _emitTypeName(clause.exceptionType.type), |
| 1833 ]), then, otherwise); |
1800 } | 1834 } |
1801 | 1835 |
1802 JS.Statement _statement(Iterable stmts) { | 1836 JS.Statement _statement(Iterable stmts) { |
1803 var s = stmts is List ? stmts : new List<JS.Statement>.from(stmts); | 1837 var s = stmts is List ? stmts : new List<JS.Statement>.from(stmts); |
1804 // TODO(jmesserly): empty block singleton? | 1838 // TODO(jmesserly): empty block singleton? |
1805 if (s.length == 0) return new JS.Block([]); | 1839 if (s.length == 0) return new JS.Block([]); |
1806 if (s.length == 1) return s[0]; | 1840 if (s.length == 1) return s[0]; |
1807 return new JS.Block(s); | 1841 return new JS.Block(s); |
1808 } | 1842 } |
1809 | 1843 |
| 1844 /// Visits the catch clause body. This skips the exception type guard, if any. |
| 1845 /// That is handled in [_visitCatch]. |
1810 @override | 1846 @override |
1811 JS.Statement visitCatchClause(CatchClause node) { | 1847 JS.Statement visitCatchClause(CatchClause node) { |
1812 var body = []; | 1848 var body = []; |
1813 | 1849 |
1814 var savedCatch = _catchParameter; | 1850 var savedCatch = _catchParameter; |
1815 if (node.catchKeyword != null) { | 1851 if (node.catchKeyword != null) { |
1816 var name = node.exceptionParameter; | 1852 var name = node.exceptionParameter; |
1817 if (name != null && name.name != _catchParameter) { | 1853 if (name != null && name != _catchParameter) { |
1818 var decl = new JS.Identifier(name.name); | 1854 body.add(js.statement( |
1819 decl.sourceInformation = name; | 1855 'let # = #;', [_visit(name), _visit(_catchParameter)])); |
1820 body.add(js.statement('let # = #;', [decl, _catchParameter])); | 1856 _catchParameter = name; |
1821 _catchParameter = name.name; | |
1822 } | 1857 } |
1823 if (node.stackTraceParameter != null) { | 1858 if (node.stackTraceParameter != null) { |
1824 var stackVar = node.stackTraceParameter.name; | 1859 var stackVar = node.stackTraceParameter.name; |
1825 body.add(js.statement( | 1860 body.add(js.statement( |
1826 'let # = dart.stackTrace(#);', [stackVar, _visit(name)])); | 1861 'let # = dart.stackTrace(#);', [stackVar, _visit(name)])); |
1827 } | 1862 } |
1828 } | 1863 } |
1829 | 1864 |
1830 body.add(_visit(node.body)); | 1865 body.add(_visit(node.body)); |
1831 _catchParameter = savedCatch; | 1866 _catchParameter = savedCatch; |
1832 | |
1833 if (node.exceptionType != null) { | |
1834 return js.statement('if (dart.is(#, #)) #;', [ | |
1835 _catchParameter, | |
1836 _emitTypeName(node.exceptionType.type), | |
1837 _statement(body) | |
1838 ]); | |
1839 } | |
1840 | |
1841 return _statement(body); | 1867 return _statement(body); |
1842 } | 1868 } |
1843 | 1869 |
1844 @override | 1870 @override |
1845 JS.Case visitSwitchCase(SwitchCase node) { | 1871 JS.Case visitSwitchCase(SwitchCase node) { |
1846 var expr = _visit(node.expression); | 1872 var expr = _visit(node.expression); |
1847 var body = _visitList(node.statements); | 1873 var body = _visitList(node.statements); |
1848 if (node.labels.isNotEmpty) { | 1874 if (node.labels.isNotEmpty) { |
1849 body.insert(0, js.comment('Unimplemented case labels: ${node.labels}')); | 1875 body.insert(0, js.comment('Unimplemented case labels: ${node.labels}')); |
1850 } | 1876 } |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2064 /// | 2090 /// |
2065 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed | 2091 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed |
2066 /// for this transformation to happen, otherwise binary minus is assumed. | 2092 /// for this transformation to happen, otherwise binary minus is assumed. |
2067 /// | 2093 /// |
2068 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 2094 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
2069 /// helper, that checks for null. The user defined method is called '=='. | 2095 /// helper, that checks for null. The user defined method is called '=='. |
2070 /// | 2096 /// |
2071 JS.Expression _jsMemberName(String name, {bool unary: false}) { | 2097 JS.Expression _jsMemberName(String name, {bool unary: false}) { |
2072 if (name.startsWith('_')) { | 2098 if (name.startsWith('_')) { |
2073 if (_privateNames.add(name)) _pendingPrivateNames.add(name); | 2099 if (_privateNames.add(name)) _pendingPrivateNames.add(name); |
2074 return new JS.Identifier(name); | 2100 return new JSTemporary(name); |
2075 } | 2101 } |
2076 if (name == '[]') { | 2102 if (name == '[]') { |
2077 name = 'get'; | 2103 name = 'get'; |
2078 } else if (name == '[]=') { | 2104 } else if (name == '[]=') { |
2079 name = 'set'; | 2105 name = 'set'; |
2080 } else if (unary && name == '-') { | 2106 } else if (unary && name == '-') { |
2081 name = 'unary-'; | 2107 name = 'unary-'; |
2082 } | 2108 } |
2083 return _propertyName(name); | 2109 return _propertyName(name); |
2084 } | 2110 } |
2085 | 2111 |
2086 bool _externalOrNative(node) => | 2112 bool _externalOrNative(node) => |
2087 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 2113 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
2088 | 2114 |
2089 FunctionBody _functionBody(node) => | 2115 FunctionBody _functionBody(node) => |
2090 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 2116 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
2091 | 2117 |
2092 /// Choose a canonical name from the library element. | 2118 /// Choose a canonical name from the library element. |
2093 /// This never uses the library's name (the identifier in the `library` | 2119 /// This never uses the library's name (the identifier in the `library` |
2094 /// declaration) as it doesn't have any meaningful rules enforced. | 2120 /// declaration) as it doesn't have any meaningful rules enforced. |
2095 String _libraryName(LibraryElement library) { | 2121 JS.Identifier _libraryName(LibraryElement library) { |
2096 if (library == libraryInfo.library) return _EXPORTS; | 2122 if (library == libraryInfo.library) return _exportsVar; |
2097 return jsLibraryName(library); | 2123 return new JS.Identifier(jsLibraryName(library)); |
2098 } | 2124 } |
2099 | 2125 |
2100 String _maybeBindThis(node) { | 2126 String _maybeBindThis(node) { |
2101 if (currentClass == null) return ''; | 2127 if (currentClass == null) return ''; |
2102 var visitor = _BindThisVisitor._instance; | 2128 var visitor = _BindThisVisitor._instance; |
2103 visitor._bindThis = false; | 2129 visitor._bindThis = false; |
2104 node.accept(visitor); | 2130 node.accept(visitor); |
2105 return visitor._bindThis ? '.bind(this)' : ''; | 2131 return visitor._bindThis ? '.bind(this)' : ''; |
2106 } | 2132 } |
2107 | 2133 |
2108 /// The name for the library's exports inside itself. | |
2109 /// This much be a constant because we interpolate it into template strings, | |
2110 /// and otherwise it would break caching for them. | |
2111 /// `exports` was chosen as the most similar to ES module patterns. | |
2112 static const String _EXPORTS = 'exports'; | |
2113 | |
2114 static bool _needsImplicitThis(Element e) => | 2134 static bool _needsImplicitThis(Element e) => |
2115 e is PropertyAccessorElement && !e.variable.isStatic || | 2135 e is PropertyAccessorElement && !e.variable.isStatic || |
2116 e is ClassMemberElement && !e.isStatic && e is! ConstructorElement; | 2136 e is ClassMemberElement && !e.isStatic && e is! ConstructorElement; |
2117 } | 2137 } |
2118 | 2138 |
2119 /// Returns true if the local variable is potentially mutated within [context]. | 2139 /// Returns true if the local variable is potentially mutated within [context]. |
2120 /// This accounts for closures that may have been created outside of [context]. | 2140 /// This accounts for closures that may have been created outside of [context]. |
2121 bool _isPotentiallyMutated(VariableElementImpl e, [AstNode context]) { | 2141 bool _isPotentiallyMutated(VariableElementImpl e, [AstNode context]) { |
2122 if (e.isPotentiallyMutatedInClosure) { | 2142 if (e.isPotentiallyMutatedInClosure) { |
2123 // TODO(jmesserly): this returns true incorrectly in some cases, because | 2143 // TODO(jmesserly): this returns true incorrectly in some cases, because |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2211 return computeHash(text); | 2231 return computeHash(text); |
2212 } else { | 2232 } else { |
2213 var text = jsNodeToString(jsTree); | 2233 var text = jsNodeToString(jsTree); |
2214 new File(outputPath).writeAsStringSync(text); | 2234 new File(outputPath).writeAsStringSync(text); |
2215 return computeHash(text); | 2235 return computeHash(text); |
2216 } | 2236 } |
2217 } | 2237 } |
2218 } | 2238 } |
2219 | 2239 |
2220 void _writeNode(JS.JavaScriptPrintingContext context, JS.Node node) { | 2240 void _writeNode(JS.JavaScriptPrintingContext context, JS.Node node) { |
2221 var opts = new JS.JavaScriptPrintingOptions(avoidKeywordsInIdentifiers: true); | 2241 var opts = new JS.JavaScriptPrintingOptions(allowKeywordsInProperties: true); |
2222 node.accept(new JS.Printer(opts, context)); | 2242 node.accept(new JS.Printer(opts, context, localNamer: new JSNamer(node))); |
2223 } | 2243 } |
2224 | 2244 |
2225 /// This is a debugging helper to print a JS node. | 2245 /// This is a debugging helper to print a JS node. |
2226 String jsNodeToString(JS.Node node) { | 2246 String jsNodeToString(JS.Node node) { |
2227 var context = new JS.SimpleJavaScriptPrintingContext(); | 2247 var context = new JS.SimpleJavaScriptPrintingContext(); |
2228 _writeNode(context, node); | 2248 _writeNode(context, node); |
2229 return context.getText(); | 2249 return context.getText(); |
2230 } | 2250 } |
2231 | 2251 |
2232 /// Choose a canonical name from the library element. | 2252 /// Choose a canonical name from the library element. |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2309 | 2329 |
2310 // TODO(jmesserly): in many cases marking the end will be unncessary. | 2330 // TODO(jmesserly): in many cases marking the end will be unncessary. |
2311 printer.mark(_location(node.end)); | 2331 printer.mark(_location(node.end)); |
2312 } | 2332 } |
2313 | 2333 |
2314 String _getIdentifier(AstNode node) { | 2334 String _getIdentifier(AstNode node) { |
2315 if (node is SimpleIdentifier) return node.name; | 2335 if (node is SimpleIdentifier) return node.name; |
2316 return null; | 2336 return null; |
2317 } | 2337 } |
2318 } | 2338 } |
OLD | NEW |