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

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

Issue 1030063004: more care around generated names, fixes #60 #82 and #97 (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
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; 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698