| 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 |