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 13 matching lines...) Expand all Loading... |
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 import 'code_generator.dart'; | 32 import 'code_generator.dart'; |
33 | 33 |
34 // This must match the optional parameter name used in runtime.js | |
35 const String _jsNamedParameterName = r'opt$'; | |
36 | |
37 bool _isAnnotationType(Annotation m, String name) => m.name.name == name; | 34 bool _isAnnotationType(Annotation m, String name) => m.name.name == name; |
38 | 35 |
39 Annotation _getAnnotation(AnnotatedNode node, String name) => node.metadata | 36 Annotation _getAnnotation(AnnotatedNode node, String name) => node.metadata |
40 .firstWhere((annotation) => _isAnnotationType(annotation, name), | 37 .firstWhere((annotation) => _isAnnotationType(annotation, name), |
41 orElse: () => null); | 38 orElse: () => null); |
42 | 39 |
43 Annotation _getJsNameAnnotation(AnnotatedNode node) => | 40 Annotation _getJsNameAnnotation(AnnotatedNode node) => |
44 _getAnnotation(node, "JsName"); | 41 _getAnnotation(node, "JsName"); |
45 | 42 |
46 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { | 43 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 | 120 |
124 var defaultValue = js.call(jsDefaultValue); | 121 var defaultValue = js.call(jsDefaultValue); |
125 return new JS.Program([ | 122 return new JS.Program([ |
126 js.statement('var #;', name), | 123 js.statement('var #;', name), |
127 js.statement("(function($_EXPORTS) { 'use strict'; #; })(# || (# = #));", | 124 js.statement("(function($_EXPORTS) { 'use strict'; #; })(# || (# = #));", |
128 [body, name, name, defaultValue]) | 125 [body, name, name, defaultValue]) |
129 ]); | 126 ]); |
130 } | 127 } |
131 | 128 |
132 JS.Statement _initPrivateSymbol(String name) => | 129 JS.Statement _initPrivateSymbol(String name) => |
133 js.statement('let # = Symbol(#);', [name, js.string(name, "'")]); | 130 js.statement('let # = $_SYMBOL(#);', [name, js.string(name, "'")]); |
| 131 |
| 132 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, |
| 133 // until we have better name tracking. |
| 134 String get _SYMBOL => currentLibrary.isDartCore ? 'dart.JsSymbol' : 'Symbol'; |
134 | 135 |
135 @override | 136 @override |
136 JS.Statement visitCompilationUnit(CompilationUnit node) { | 137 JS.Statement visitCompilationUnit(CompilationUnit node) { |
137 var source = node.element.source; | 138 var source = node.element.source; |
138 | 139 |
139 _constEvaluator = new ConstantEvaluator(source, rules.provider); | 140 _constEvaluator = new ConstantEvaluator(source, rules.provider); |
140 _checkerReporter.enterSource(source); | 141 _checkerReporter.enterSource(source); |
141 | 142 |
142 // TODO(jmesserly): scriptTag, directives. | 143 // TODO(jmesserly): scriptTag, directives. |
143 var body = <JS.Statement>[]; | 144 var body = <JS.Statement>[]; |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
252 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { | 253 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { |
253 // If we've already emitted this class, skip it. | 254 // If we've already emitted this class, skip it. |
254 var classElem = node.element; | 255 var classElem = node.element; |
255 if (_pendingClasses.remove(classElem) == null) return null; | 256 if (_pendingClasses.remove(classElem) == null) return null; |
256 | 257 |
257 var name = node.name.name; | 258 var name = node.name.name; |
258 var heritage = | 259 var heritage = |
259 js.call('dart.mixin(#)', [_visitList(node.withClause.mixinTypes)]); | 260 js.call('dart.mixin(#)', [_visitList(node.withClause.mixinTypes)]); |
260 var classDecl = new JS.ClassDeclaration( | 261 var classDecl = new JS.ClassDeclaration( |
261 new JS.ClassExpression(new JS.VariableDeclaration(name), heritage, [])); | 262 new JS.ClassExpression(new JS.VariableDeclaration(name), heritage, [])); |
262 if (isPublic(name)) _exports.add(name); | |
263 | 263 |
264 return _finishClassDef(classElem, classDecl); | 264 return _finishClassDef(classElem, classDecl); |
265 } | 265 } |
266 | 266 |
267 @override | 267 @override |
268 JS.Statement visitClassDeclaration(ClassDeclaration node) { | 268 JS.Statement visitClassDeclaration(ClassDeclaration node) { |
269 // If we've already emitted this class, skip it. | 269 // If we've already emitted this class, skip it. |
270 var classElem = node.element; | 270 var classElem = node.element; |
271 if (_pendingClasses.remove(classElem) == null) return null; | 271 if (_pendingClasses.remove(classElem) == null) return null; |
272 if (_getJsNameAnnotation(node) != null) return null; | 272 if (_getJsNameAnnotation(node) != null) return null; |
273 | 273 |
274 currentClass = node; | 274 currentClass = node; |
275 | 275 |
276 var name = classElem.name; | 276 var name = classElem.name; |
277 if (isPublic(name)) _exports.add(name); | |
278 | 277 |
279 var ctors = <ConstructorDeclaration>[]; | 278 var ctors = <ConstructorDeclaration>[]; |
280 var fields = <FieldDeclaration>[]; | 279 var fields = <FieldDeclaration>[]; |
281 var staticFields = <FieldDeclaration>[]; | 280 var staticFields = <FieldDeclaration>[]; |
282 for (var member in node.members) { | 281 for (var member in node.members) { |
283 if (member is ConstructorDeclaration) { | 282 if (member is ConstructorDeclaration) { |
284 ctors.add(member); | 283 ctors.add(member); |
285 } else if (member is FieldDeclaration) { | 284 } else if (member is FieldDeclaration) { |
286 (member.isStatic ? staticFields : fields).add(member); | 285 (member.isStatic ? staticFields : fields).add(member); |
287 } | 286 } |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
334 | 333 |
335 return js.statement( | 334 return js.statement( |
336 'dart.defineLazyClass(#, { get #() { #; return #; } });', [ | 335 'dart.defineLazyClass(#, { get #() { #; return #; } });', [ |
337 _EXPORTS, | 336 _EXPORTS, |
338 name, | 337 name, |
339 body, | 338 body, |
340 name | 339 name |
341 ]); | 340 ]); |
342 } | 341 } |
343 | 342 |
| 343 if (isPublic(name)) _exports.add(name); |
| 344 |
344 if (genericDef != null) { | 345 if (genericDef != null) { |
345 body = js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); | 346 body = js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); |
| 347 if (isPublic(name)) _exports.add(genericName); |
346 } | 348 } |
347 | 349 |
348 if (classElem.type.isObject) return body; | 350 if (classElem.type.isObject) return body; |
349 | 351 |
350 // If we're not lazy, we still need to ensure our dependencies are | 352 // If we're not lazy, we still need to ensure our dependencies are |
351 // generated first. | 353 // generated first. |
352 var classDefs = <JS.Statement>[]; | 354 var classDefs = <JS.Statement>[]; |
353 _emitClassIfNeeded(classDefs, classElem.supertype.element); | 355 _emitClassIfNeeded(classDefs, classElem.supertype.element); |
354 for (var m in classElem.mixins) { | 356 for (var m in classElem.mixins) { |
355 _emitClassIfNeeded(classDefs, m.element); | 357 _emitClassIfNeeded(classDefs, m.element); |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
450 if (result) break; | 452 if (result) break; |
451 result = _inLibraryCycle(e.exportedLibrary); | 453 result = _inLibraryCycle(e.exportedLibrary); |
452 } | 454 } |
453 return _libraryCycleMemo[library] = result; | 455 return _libraryCycleMemo[library] = result; |
454 } | 456 } |
455 | 457 |
456 JS.Statement _emitGenericClassDef(ClassElement cls, JS.Statement body) { | 458 JS.Statement _emitGenericClassDef(ClassElement cls, JS.Statement body) { |
457 var name = cls.name; | 459 var name = cls.name; |
458 var genericName = '$name\$'; | 460 var genericName = '$name\$'; |
459 var typeParams = cls.typeParameters.map((p) => new JS.Parameter(p.name)); | 461 var typeParams = cls.typeParameters.map((p) => new JS.Parameter(p.name)); |
460 // TODO(jmesserly): is it worth exporting both names? Alternatively we could | |
461 // put the generic type constructor on the <dynamic> instance. | |
462 if (isPublic(name)) _exports.add(genericName); | |
463 return js.statement('let # = dart.generic(function(#) { #; return #; });', [ | 462 return js.statement('let # = dart.generic(function(#) { #; return #; });', [ |
464 genericName, | 463 genericName, |
465 typeParams, | 464 typeParams, |
466 body, | 465 body, |
467 name | 466 name |
468 ]); | 467 ]); |
469 } | 468 } |
470 | 469 |
471 JS.Expression _classHeritage(ClassDeclaration node) { | 470 JS.Expression _classHeritage(ClassDeclaration node) { |
472 if (node.element.type.isObject) return null; | 471 if (node.element.type.isObject) return null; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
514 if (element.library.isDartCore && element.name == 'Iterable') { | 513 if (element.library.isDartCore && element.name == 'Iterable') { |
515 JS.Fun body = js.call('''function() { | 514 JS.Fun body = js.call('''function() { |
516 var iterator = this.iterator; | 515 var iterator = this.iterator; |
517 return { | 516 return { |
518 next() { | 517 next() { |
519 var done = iterator.moveNext(); | 518 var done = iterator.moveNext(); |
520 return { done: done, current: done ? void 0 : iterator.current }; | 519 return { done: done, current: done ? void 0 : iterator.current }; |
521 } | 520 } |
522 }; | 521 }; |
523 }'''); | 522 }'''); |
524 jsMethods.add(new JS.Method(js.call('Symbol.iterator'), body)); | 523 jsMethods.add(new JS.Method(js.call('$_SYMBOL.iterator'), body)); |
525 } | 524 } |
526 return jsMethods.where((m) => m != null).toList(growable: false); | 525 return jsMethods.where((m) => m != null).toList(growable: false); |
527 } | 526 } |
528 | 527 |
529 /// Emit class members that need to come after the class declaration, such | 528 /// Emit class members that need to come after the class declaration, such |
530 /// as static fields. See [_emitClassMethods] for things that are emitted | 529 /// as static fields. See [_emitClassMethods] for things that are emitted |
531 /// insite the ES6 `class { ... }` node. | 530 /// insite the ES6 `class { ... }` node. |
532 JS.Statement _finishClassMembers(String name, JS.ClassExpression cls, | 531 JS.Statement _finishClassMembers(String name, JS.ClassExpression cls, |
533 List<ConstructorDeclaration> ctors, List<FieldDeclaration> staticFields) { | 532 List<ConstructorDeclaration> ctors, List<FieldDeclaration> staticFields) { |
534 var body = <JS.Statement>[]; | 533 var body = <JS.Statement>[]; |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
591 node.name == null) { | 590 node.name == null) { |
592 // Implements Dart constructor behavior. Because of V8 `super` | 591 // Implements Dart constructor behavior. Because of V8 `super` |
593 // [constructor restrictions] | 592 // [constructor restrictions] |
594 // (https://code.google.com/p/v8/issues/detail?id=3330#c65) | 593 // (https://code.google.com/p/v8/issues/detail?id=3330#c65) |
595 // we cannot currently emit actual ES6 constructors with super calls. | 594 // we cannot currently emit actual ES6 constructors with super calls. |
596 // Instead we use the same trick as named constructors, and do them as | 595 // Instead we use the same trick as named constructors, and do them as |
597 // instance methods that perform initialization. | 596 // instance methods that perform initialization. |
598 // TODO(jmesserly): we'll need to rethink this once the ES6 spec and V8 | 597 // TODO(jmesserly): we'll need to rethink this once the ES6 spec and V8 |
599 // settles. See <https://github.com/dart-lang/dev_compiler/issues/51>. | 598 // settles. See <https://github.com/dart-lang/dev_compiler/issues/51>. |
600 // Performance of this pattern is likely to be bad. | 599 // Performance of this pattern is likely to be bad. |
| 600 name = 'constructor'; |
601 body = js.statement('''{ | 601 body = js.statement('''{ |
602 // Get the class name for this instance. | 602 // Get the class name for this instance. |
603 var name = this.constructor.name; | 603 var name = this.constructor.name; |
604 // Call the default constructor. | 604 // Call the default constructor. |
605 var init = this[name]; | 605 var init = this[name]; |
606 var result = void 0; | 606 var result = void 0; |
607 if (init) result = init.apply(this, arguments); | 607 if (init) result = init.apply(this, arguments); |
608 return result === void 0 ? this : result; | 608 return result === void 0 ? this : result; |
609 }'''); | 609 }'''); |
610 } else { | 610 } else { |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
790 if (parameters == null || !_hasArgumentInitializers(parameters)) { | 790 if (parameters == null || !_hasArgumentInitializers(parameters)) { |
791 return null; | 791 return null; |
792 } | 792 } |
793 | 793 |
794 var body = []; | 794 var body = []; |
795 for (var param in parameters.parameters) { | 795 for (var param in parameters.parameters) { |
796 // TODO(justinfagnani): rename identifier if necessary | 796 // TODO(justinfagnani): rename identifier if necessary |
797 var name = param.identifier.name; | 797 var name = param.identifier.name; |
798 | 798 |
799 if (param.kind == ParameterKind.NAMED) { | 799 if (param.kind == ParameterKind.NAMED) { |
800 body.add(js.statement('let # = opt\$.# === void 0 ? # : opt\$.#;', [ | 800 body.add(js.statement('let # = opt\$ && # in opt\$ ? opt\$.# : #;', [ |
801 name, | 801 name, |
| 802 js.string(name, "'"), |
802 name, | 803 name, |
803 _defaultParamValue(param), | 804 _defaultParamValue(param), |
804 name | |
805 ])); | 805 ])); |
806 } else if (param.kind == ParameterKind.POSITIONAL) { | 806 } else if (param.kind == ParameterKind.POSITIONAL) { |
807 body.add(js.statement('if (# === void 0) # = #;', [ | 807 body.add(js.statement('if (# === void 0) # = #;', [ |
808 name, | 808 name, |
809 name, | 809 name, |
810 _defaultParamValue(param) | 810 _defaultParamValue(param) |
811 ])); | 811 ])); |
812 } | 812 } |
813 } | 813 } |
814 return _statement(body); | 814 return _statement(body); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
858 | 858 |
859 body.add(new JS.FunctionDeclaration( | 859 body.add(new JS.FunctionDeclaration( |
860 new JS.VariableDeclaration(name), _visit(node.functionExpression))); | 860 new JS.VariableDeclaration(name), _visit(node.functionExpression))); |
861 | 861 |
862 if (isPublic(name)) _exports.add(name); | 862 if (isPublic(name)) _exports.add(name); |
863 return _statement(body); | 863 return _statement(body); |
864 } | 864 } |
865 | 865 |
866 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { | 866 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { |
867 var name = node.name.name; | 867 var name = node.name.name; |
868 if (isPublic(name)) _exports.add(name); | |
869 return new JS.Method( | 868 return new JS.Method( |
870 new JS.PropertyName(name), _visit(node.functionExpression), | 869 new JS.PropertyName(name), _visit(node.functionExpression), |
871 isGetter: node.isGetter, isSetter: node.isSetter); | 870 isGetter: node.isGetter, isSetter: node.isSetter); |
872 } | 871 } |
873 | 872 |
874 @override | 873 @override |
875 JS.Expression visitFunctionExpression(FunctionExpression node) { | 874 JS.Expression visitFunctionExpression(FunctionExpression node) { |
876 var params = _visit(node.parameters); | 875 var params = _visit(node.parameters); |
877 if (params == null) params = []; | 876 if (params == null) params = []; |
878 | 877 |
(...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1153 assert(node.parent is ArgumentList); | 1152 assert(node.parent is ArgumentList); |
1154 return new JS.Property( | 1153 return new JS.Property( |
1155 new JS.PropertyName(node.name.label.name), _visit(node.expression)); | 1154 new JS.PropertyName(node.name.label.name), _visit(node.expression)); |
1156 } | 1155 } |
1157 | 1156 |
1158 @override | 1157 @override |
1159 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) { | 1158 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) { |
1160 var result = <JS.Parameter>[]; | 1159 var result = <JS.Parameter>[]; |
1161 for (FormalParameter param in node.parameters) { | 1160 for (FormalParameter param in node.parameters) { |
1162 if (param.kind == ParameterKind.NAMED) { | 1161 if (param.kind == ParameterKind.NAMED) { |
1163 result.add(new JS.Parameter(_jsNamedParameterName)); | 1162 result.add(new JS.Parameter(r'opt$')); |
1164 break; | 1163 break; |
1165 } | 1164 } |
1166 result.add(_visit(param)); | 1165 result.add(_visit(param)); |
1167 } | 1166 } |
1168 return result; | 1167 return result; |
1169 } | 1168 } |
1170 | 1169 |
1171 @override | 1170 @override |
1172 JS.Statement visitExpressionStatement(ExpressionStatement node) => | 1171 JS.Statement visitExpressionStatement(ExpressionStatement node) => |
1173 _expressionStatement(_visit(node.expression)); | 1172 _expressionStatement(_visit(node.expression)); |
(...skipping 1127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2301 | 2300 |
2302 // TODO(jmesserly): in many cases marking the end will be unncessary. | 2301 // TODO(jmesserly): in many cases marking the end will be unncessary. |
2303 printer.mark(_location(node.end)); | 2302 printer.mark(_location(node.end)); |
2304 } | 2303 } |
2305 | 2304 |
2306 String _getIdentifier(AstNode node) { | 2305 String _getIdentifier(AstNode node) { |
2307 if (node is SimpleIdentifier) return node.name; | 2306 if (node is SimpleIdentifier) return node.name; |
2308 return null; | 2307 return null; |
2309 } | 2308 } |
2310 } | 2309 } |
OLD | NEW |