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, SplayTreeSet; | 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; |
8 | 8 |
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
(...skipping 13 matching lines...) Expand all Loading... |
24 import '../js/js_ast.dart' show js; | 24 import '../js/js_ast.dart' show js; |
25 | 25 |
26 import '../closure/closure_annotator.dart' show ClosureAnnotator; | 26 import '../closure/closure_annotator.dart' show ClosureAnnotator; |
27 import '../compiler.dart' show AbstractCompiler; | 27 import '../compiler.dart' show AbstractCompiler; |
28 import '../info.dart'; | 28 import '../info.dart'; |
29 import '../options.dart' show CodegenOptions; | 29 import '../options.dart' show CodegenOptions; |
30 import '../utils.dart'; | 30 import '../utils.dart'; |
31 | 31 |
32 import 'code_generator.dart'; | 32 import 'code_generator.dart'; |
33 import 'js_field_storage.dart'; | 33 import 'js_field_storage.dart'; |
| 34 import 'js_interop.dart'; |
34 import 'js_names.dart' as JS; | 35 import 'js_names.dart' as JS; |
35 import 'js_metalet.dart' as JS; | 36 import 'js_metalet.dart' as JS; |
36 import 'js_module_item_order.dart'; | 37 import 'js_module_item_order.dart'; |
37 import 'js_printer.dart' show writeJsLibrary; | 38 import 'js_printer.dart' show writeJsLibrary; |
38 import 'side_effect_analysis.dart'; | 39 import 'side_effect_analysis.dart'; |
39 | 40 |
40 // Various dynamic helpers we call. | 41 // Various dynamic helpers we call. |
41 // If renaming these, make sure to check other places like the | 42 // If renaming these, make sure to check other places like the |
42 // _runtime.js file and comments. | 43 // _runtime.js file and comments. |
43 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can | 44 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can |
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
240 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); | 241 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); |
241 | 242 |
242 var code = _visit(node); | 243 var code = _visit(node); |
243 if (code != null) _moduleItems.add(code); | 244 if (code != null) _moduleItems.add(code); |
244 } | 245 } |
245 | 246 |
246 @override | 247 @override |
247 void visitLibraryDirective(LibraryDirective node) { | 248 void visitLibraryDirective(LibraryDirective node) { |
248 assert(_jsModuleValue == null); | 249 assert(_jsModuleValue == null); |
249 | 250 |
250 var jsName = findAnnotation(node.element, _isJsNameAnnotation); | 251 var jsName = findAnnotation(node.element, isJsNameAnnotation); |
251 _jsModuleValue = | 252 _jsModuleValue = |
252 getConstantField(jsName, 'name', types.stringType)?.toStringValue(); | 253 getConstantField(jsName, 'name', types.stringType)?.toStringValue(); |
253 } | 254 } |
254 | 255 |
255 @override | 256 @override |
256 void visitImportDirective(ImportDirective node) { | 257 void visitImportDirective(ImportDirective node) { |
257 // Nothing to do yet, but we'll want to convert this to an ES6 import once | 258 // Nothing to do yet, but we'll want to convert this to an ES6 import once |
258 // we have support for modules. | 259 // we have support for modules. |
259 } | 260 } |
260 | 261 |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
413 if (isPublic(dartClassName)) _addExport(dartClassName); | 414 if (isPublic(dartClassName)) _addExport(dartClassName); |
414 return js.statement('let # = #;', [dartClassName, jsTypeName]); | 415 return js.statement('let # = #;', [dartClassName, jsTypeName]); |
415 } | 416 } |
416 return null; | 417 return null; |
417 } | 418 } |
418 | 419 |
419 @override | 420 @override |
420 JS.Statement visitClassDeclaration(ClassDeclaration node) { | 421 JS.Statement visitClassDeclaration(ClassDeclaration node) { |
421 var classElem = node.element; | 422 var classElem = node.element; |
422 var type = classElem.type; | 423 var type = classElem.type; |
423 var jsName = findAnnotation(classElem, _isJsNameAnnotation); | 424 var jsName = findAnnotation(classElem, isJsNameAnnotation); |
424 | 425 |
425 if (jsName != null) return _emitJsType(node.name.name, jsName); | 426 if (jsName != null) return _emitJsType(node.name.name, jsName); |
426 | 427 |
427 var ctors = <ConstructorDeclaration>[]; | 428 var ctors = <ConstructorDeclaration>[]; |
428 var fields = <FieldDeclaration>[]; | 429 var fields = <FieldDeclaration>[]; |
429 var methods = <MethodDeclaration>[]; | 430 var methods = <MethodDeclaration>[]; |
430 for (var member in node.members) { | 431 for (var member in node.members) { |
431 if (member is ConstructorDeclaration) { | 432 if (member is ConstructorDeclaration) { |
432 ctors.add(member); | 433 ctors.add(member); |
433 } else if (member is FieldDeclaration && !member.isStatic) { | 434 } else if (member is FieldDeclaration && !member.isStatic) { |
434 fields.add(member); | 435 fields.add(member); |
435 } else if (member is MethodDeclaration) { | 436 } else if (member is MethodDeclaration) { |
436 methods.add(member); | 437 methods.add(member); |
437 } | 438 } |
438 } | 439 } |
439 | 440 |
440 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), | 441 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), |
441 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); | 442 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); |
442 | 443 |
443 String jsPeerName; | 444 String jsPeerName; |
444 var jsPeer = findAnnotation(classElem, _isJsPeerInterface); | 445 var jsPeer = findAnnotation(classElem, isJsPeerInterface); |
445 if (jsPeer != null) { | 446 if (jsPeer != null) { |
446 jsPeerName = | 447 jsPeerName = |
447 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue(); | 448 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue(); |
448 } | 449 } |
449 | 450 |
450 var body = _finishClassMembers(classElem, classExpr, ctors, fields, methods, | 451 var body = _finishClassMembers(classElem, classExpr, ctors, fields, methods, |
451 node.metadata, jsPeerName); | 452 node.metadata, jsPeerName); |
452 | 453 |
453 var result = _finishClassDef(type, body); | 454 var result = _finishClassDef(type, body); |
454 | 455 |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
589 var type = element.type; | 590 var type = element.type; |
590 var isObject = type.isObject; | 591 var isObject = type.isObject; |
591 | 592 |
592 // Iff no constructor is specified for a class C, it implicitly has a | 593 // Iff no constructor is specified for a class C, it implicitly has a |
593 // default constructor `C() : super() {}`, unless C is class Object. | 594 // default constructor `C() : super() {}`, unless C is class Object. |
594 var jsMethods = <JS.Method>[]; | 595 var jsMethods = <JS.Method>[]; |
595 if (ctors.isEmpty && !isObject) { | 596 if (ctors.isEmpty && !isObject) { |
596 jsMethods.add(_emitImplicitConstructor(node, fields)); | 597 jsMethods.add(_emitImplicitConstructor(node, fields)); |
597 } | 598 } |
598 | 599 |
599 bool hasJsPeer = findAnnotation(element, _isJsPeerInterface) != null; | 600 bool hasJsPeer = findAnnotation(element, isJsPeerInterface) != null; |
600 | 601 |
601 bool hasIterator = false; | 602 bool hasIterator = false; |
602 for (var m in node.members) { | 603 for (var m in node.members) { |
603 if (m is ConstructorDeclaration) { | 604 if (m is ConstructorDeclaration) { |
604 jsMethods.add(_emitConstructor(m, type, fields, isObject)); | 605 jsMethods.add(_emitConstructor(m, type, fields, isObject)); |
605 } else if (m is MethodDeclaration) { | 606 } else if (m is MethodDeclaration) { |
606 jsMethods.add(_emitMethodDeclaration(type, m)); | 607 jsMethods.add(_emitMethodDeclaration(type, m)); |
607 | 608 |
608 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { | 609 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { |
609 hasIterator = true; | 610 hasIterator = true; |
(...skipping 1236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1846 return js.call(code, [_visit(node.function), _visit(node.argumentList)]); | 1847 return js.call(code, [_visit(node.function), _visit(node.argumentList)]); |
1847 } | 1848 } |
1848 | 1849 |
1849 @override | 1850 @override |
1850 List<JS.Expression> visitArgumentList(ArgumentList node) { | 1851 List<JS.Expression> visitArgumentList(ArgumentList node) { |
1851 var args = <JS.Expression>[]; | 1852 var args = <JS.Expression>[]; |
1852 var named = <JS.Property>[]; | 1853 var named = <JS.Property>[]; |
1853 for (var arg in node.arguments) { | 1854 for (var arg in node.arguments) { |
1854 if (arg is NamedExpression) { | 1855 if (arg is NamedExpression) { |
1855 named.add(_visit(arg)); | 1856 named.add(_visit(arg)); |
| 1857 } else if (arg is MethodInvocation && isJsSpreadInvocation(arg)) { |
| 1858 args.add( |
| 1859 new JS.RestParameter(_visit(arg.argumentList.arguments.single))); |
1856 } else { | 1860 } else { |
1857 args.add(_visit(arg)); | 1861 args.add(_visit(arg)); |
1858 } | 1862 } |
1859 } | 1863 } |
1860 if (named.isNotEmpty) { | 1864 if (named.isNotEmpty) { |
1861 args.add(new JS.ObjectInitializer(named)); | 1865 args.add(new JS.ObjectInitializer(named)); |
1862 } | 1866 } |
1863 return args; | 1867 return args; |
1864 } | 1868 } |
1865 | 1869 |
1866 @override | 1870 @override |
1867 JS.Property visitNamedExpression(NamedExpression node) { | 1871 JS.Property visitNamedExpression(NamedExpression node) { |
1868 assert(node.parent is ArgumentList); | 1872 assert(node.parent is ArgumentList); |
1869 return new JS.Property( | 1873 return new JS.Property( |
1870 _propertyName(node.name.label.name), _visit(node.expression)); | 1874 _propertyName(node.name.label.name), _visit(node.expression)); |
1871 } | 1875 } |
1872 | 1876 |
1873 @override | 1877 @override |
1874 List<JS.Identifier> visitFormalParameterList(FormalParameterList node) { | 1878 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) { |
1875 var result = <JS.Identifier>[]; | 1879 var result = <JS.Parameter>[]; |
1876 for (FormalParameter param in node.parameters) { | 1880 for (FormalParameter param in node.parameters) { |
1877 if (param.kind == ParameterKind.NAMED) { | 1881 if (param.kind == ParameterKind.NAMED) { |
1878 result.add(_namedArgTemp); | 1882 result.add(_namedArgTemp); |
1879 break; | 1883 break; |
1880 } | 1884 } |
1881 result.add(_visit(param)); | 1885 result.add(_visit(param)); |
1882 } | 1886 } |
1883 return result; | 1887 return result; |
1884 } | 1888 } |
1885 | 1889 |
(...skipping 626 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2512 _cascadeTarget = savedCascadeTemp; | 2516 _cascadeTarget = savedCascadeTemp; |
2513 return result; | 2517 return result; |
2514 } | 2518 } |
2515 | 2519 |
2516 @override | 2520 @override |
2517 visitParenthesizedExpression(ParenthesizedExpression node) => | 2521 visitParenthesizedExpression(ParenthesizedExpression node) => |
2518 // The printer handles precedence so we don't need to. | 2522 // The printer handles precedence so we don't need to. |
2519 _visit(node.expression); | 2523 _visit(node.expression); |
2520 | 2524 |
2521 @override | 2525 @override |
2522 visitFormalParameter(FormalParameter node) => | 2526 visitFormalParameter(FormalParameter node) { |
2523 visitSimpleIdentifier(node.identifier); | 2527 var id = visitSimpleIdentifier(node.identifier); |
| 2528 |
| 2529 var isRestArg = findAnnotation(node.element, isJsRestAnnotation) != null; |
| 2530 return isRestArg ? new JS.RestParameter(id) : id; |
| 2531 } |
2524 | 2532 |
2525 @override | 2533 @override |
2526 JS.This visitThisExpression(ThisExpression node) => new JS.This(); | 2534 JS.This visitThisExpression(ThisExpression node) => new JS.This(); |
2527 | 2535 |
2528 @override | 2536 @override |
2529 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); | 2537 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); |
2530 | 2538 |
2531 @override | 2539 @override |
2532 visitPrefixedIdentifier(PrefixedIdentifier node) { | 2540 visitPrefixedIdentifier(PrefixedIdentifier node) { |
2533 if (isLibraryPrefix(node.prefix)) { | 2541 if (isLibraryPrefix(node.prefix)) { |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2675 var target = _getTarget(node); | 2683 var target = _getTarget(node); |
2676 if (_useNativeJsIndexer(target.staticType)) { | 2684 if (_useNativeJsIndexer(target.staticType)) { |
2677 return new JS.PropertyAccess(_visit(target), _visit(node.index)); | 2685 return new JS.PropertyAccess(_visit(target), _visit(node.index)); |
2678 } | 2686 } |
2679 return _emitSend(target, '[]', [node.index]); | 2687 return _emitSend(target, '[]', [node.index]); |
2680 } | 2688 } |
2681 | 2689 |
2682 // TODO(jmesserly): ideally we'd check the method and see if it is marked | 2690 // TODO(jmesserly): ideally we'd check the method and see if it is marked |
2683 // `external`, but that doesn't work because it isn't in the element model. | 2691 // `external`, but that doesn't work because it isn't in the element model. |
2684 bool _useNativeJsIndexer(DartType type) => | 2692 bool _useNativeJsIndexer(DartType type) => |
2685 findAnnotation(type.element, _isJsNameAnnotation) != null; | 2693 findAnnotation(type.element, isJsNameAnnotation) != null; |
2686 | 2694 |
2687 /// Gets the target of a [PropertyAccess], [IndexExpression], or | 2695 /// Gets the target of a [PropertyAccess], [IndexExpression], or |
2688 /// [MethodInvocation]. These three nodes can appear in a [CascadeExpression]. | 2696 /// [MethodInvocation]. These three nodes can appear in a [CascadeExpression]. |
2689 Expression _getTarget(node) { | 2697 Expression _getTarget(node) { |
2690 assert(node is IndexExpression || | 2698 assert(node is IndexExpression || |
2691 node is PropertyAccess || | 2699 node is PropertyAccess || |
2692 node is MethodInvocation); | 2700 node is MethodInvocation); |
2693 return node.isCascaded ? _cascadeTarget : node.target; | 2701 return node.isCascaded ? _cascadeTarget : node.target; |
2694 } | 2702 } |
2695 | 2703 |
(...skipping 598 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3294 /// This never uses the library's name (the identifier in the `library` | 3302 /// This never uses the library's name (the identifier in the `library` |
3295 /// declaration) as it doesn't have any meaningful rules enforced. | 3303 /// declaration) as it doesn't have any meaningful rules enforced. |
3296 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); | 3304 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); |
3297 | 3305 |
3298 /// Shorthand for identifier-like property names. | 3306 /// Shorthand for identifier-like property names. |
3299 /// For now, we emit them as strings and the printer restores them to | 3307 /// For now, we emit them as strings and the printer restores them to |
3300 /// identifiers if it can. | 3308 /// identifiers if it can. |
3301 // TODO(jmesserly): avoid the round tripping through quoted form. | 3309 // TODO(jmesserly): avoid the round tripping through quoted form. |
3302 JS.LiteralString _propertyName(String name) => js.string(name, "'"); | 3310 JS.LiteralString _propertyName(String name) => js.string(name, "'"); |
3303 | 3311 |
3304 // TODO(jmesserly): validate the library. See issue #135. | |
3305 bool _isJsNameAnnotation(DartObject value) => value.type.name == 'JsName'; | |
3306 | |
3307 bool _isJsPeerInterface(DartObject value) => | |
3308 value.type.name == 'JsPeerInterface'; | |
3309 | |
3310 // TODO(jacobr): we would like to do something like the following | 3312 // TODO(jacobr): we would like to do something like the following |
3311 // but we don't have summary support yet. | 3313 // but we don't have summary support yet. |
3312 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 3314 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
3313 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 3315 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
3314 | 3316 |
3315 /// A special kind of element created by the compiler, signifying a temporary | 3317 /// A special kind of element created by the compiler, signifying a temporary |
3316 /// variable. These objects use instance equality, and should be shared | 3318 /// variable. These objects use instance equality, and should be shared |
3317 /// everywhere in the tree where they are treated as the same variable. | 3319 /// everywhere in the tree where they are treated as the same variable. |
3318 class TemporaryVariableElement extends LocalVariableElementImpl { | 3320 class TemporaryVariableElement extends LocalVariableElementImpl { |
3319 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3321 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3320 | 3322 |
3321 int get hashCode => identityHashCode(this); | 3323 int get hashCode => identityHashCode(this); |
3322 bool operator ==(Object other) => identical(this, other); | 3324 bool operator ==(Object other) => identical(this, other); |
3323 } | 3325 } |
OLD | NEW |