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 js_codegen; | 5 library 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; |
11 import 'package:analyzer/src/generated/constant.dart'; | 11 import 'package:analyzer/src/generated/constant.dart'; |
12 import 'package:analyzer/src/generated/element.dart'; | 12 import 'package:analyzer/src/generated/element.dart'; |
| 13 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; |
13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 14 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
14 import 'package:analyzer/src/generated/scanner.dart' | 15 import 'package:analyzer/src/generated/scanner.dart' |
15 show StringToken, Token, TokenType; | 16 show StringToken, Token, TokenType; |
16 import 'package:analyzer/src/generated/type_system.dart' | 17 import 'package:analyzer/src/generated/type_system.dart' |
17 show StrongTypeSystemImpl; | 18 show StrongTypeSystemImpl; |
18 import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder; | 19 import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder; |
19 | 20 |
20 import 'ast_builder.dart' show AstBuilder; | 21 import 'ast_builder.dart' show AstBuilder; |
21 import 'reify_coercions.dart' show CoercionReifier, Tuple2; | 22 import 'reify_coercions.dart' show CoercionReifier, Tuple2; |
22 | 23 |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
211 isLazy: _isDartRuntime || !_loader.libraryIsLoaded(lib)); | 212 isLazy: _isDartRuntime || !_loader.libraryIsLoaded(lib)); |
212 }); | 213 }); |
213 | 214 |
214 // TODO(jmesserly): scriptTag support. | 215 // TODO(jmesserly): scriptTag support. |
215 // Enable this if we know we're targetting command line environment? | 216 // Enable this if we know we're targetting command line environment? |
216 // It doesn't work in browser. | 217 // It doesn't work in browser. |
217 // var jsBin = compiler.options.runnerOptions.v8Binary; | 218 // var jsBin = compiler.options.runnerOptions.v8Binary; |
218 // String scriptTag = null; | 219 // String scriptTag = null; |
219 // if (library.library.scriptTag != null) scriptTag = '/usr/bin/env $jsBin'; | 220 // if (library.library.scriptTag != null) scriptTag = '/usr/bin/env $jsBin'; |
220 return moduleBuilder.build( | 221 return moduleBuilder.build( |
221 currentModuleName, | 222 currentModuleName, _jsModuleValue, _exportsVar, items); |
222 _jsModuleValue, | |
223 _exportsVar, | |
224 items); | |
225 } | 223 } |
226 | 224 |
227 void _emitModuleItem(AstNode node) { | 225 void _emitModuleItem(AstNode node) { |
228 // Attempt to group adjacent properties. | 226 // Attempt to group adjacent properties. |
229 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); | 227 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); |
230 | 228 |
231 var code = _visit(node); | 229 var code = _visit(node); |
232 if (code != null) _moduleItems.add(code); | 230 if (code != null) _moduleItems.add(code); |
233 } | 231 } |
234 | 232 |
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
432 } | 430 } |
433 | 431 |
434 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), | 432 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), |
435 _classHeritage(classElem), _emitClassMethods(node, ctors, fields), | 433 _classHeritage(classElem), _emitClassMethods(node, ctors, fields), |
436 typeParams: _emitTypeParams(classElem).toList(), | 434 typeParams: _emitTypeParams(classElem).toList(), |
437 fields: | 435 fields: |
438 _emitFieldDeclarations(classElem, fields, staticFields).toList()); | 436 _emitFieldDeclarations(classElem, fields, staticFields).toList()); |
439 | 437 |
440 String jsPeerName; | 438 String jsPeerName; |
441 var jsPeer = findAnnotation(classElem, isJsPeerInterface); | 439 var jsPeer = findAnnotation(classElem, isJsPeerInterface); |
| 440 // Only look at "Native" annotations on registered extension types. |
| 441 // E.g., we're current ignoring the ones in dart:html. |
| 442 if (jsPeer == null && _extensionTypes.contains(classElem)) { |
| 443 jsPeer = findAnnotation(classElem, isNativeAnnotation); |
| 444 } |
442 if (jsPeer != null) { | 445 if (jsPeer != null) { |
443 jsPeerName = | 446 jsPeerName = |
444 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue(); | 447 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue(); |
| 448 if (jsPeerName.contains(',')) { |
| 449 jsPeerName = jsPeerName.split(',')[0]; |
| 450 } |
445 } | 451 } |
446 | 452 |
447 var body = _finishClassMembers(classElem, classExpr, ctors, fields, | 453 var body = _finishClassMembers(classElem, classExpr, ctors, fields, |
448 staticFields, methods, node.metadata, jsPeerName); | 454 staticFields, methods, node.metadata, jsPeerName); |
449 | 455 |
450 var result = _finishClassDef(type, body); | 456 var result = _finishClassDef(type, body); |
451 | 457 |
452 if (jsPeerName != null) { | 458 if (jsPeerName != null) { |
453 // This class isn't allowed to be lazy, because we need to set up | 459 // This class isn't allowed to be lazy, because we need to set up |
454 // the native JS type eagerly at this point. | 460 // the native JS type eagerly at this point. |
(...skipping 860 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1315 t is ParameterizedType && t.typeArguments.any(_hasUnsoundTypeParameter); | 1321 t is ParameterizedType && t.typeArguments.any(_hasUnsoundTypeParameter); |
1316 | 1322 |
1317 JS.Expression _defaultParamValue(FormalParameter param) { | 1323 JS.Expression _defaultParamValue(FormalParameter param) { |
1318 if (param is DefaultFormalParameter && param.defaultValue != null) { | 1324 if (param is DefaultFormalParameter && param.defaultValue != null) { |
1319 return _visit(param.defaultValue); | 1325 return _visit(param.defaultValue); |
1320 } else { | 1326 } else { |
1321 return new JS.LiteralNull(); | 1327 return new JS.LiteralNull(); |
1322 } | 1328 } |
1323 } | 1329 } |
1324 | 1330 |
| 1331 JS.Fun _emitNativeFunctionBody( |
| 1332 List<JS.Parameter> params, MethodDeclaration node) { |
| 1333 if (node.isStatic) { |
| 1334 // TODO(vsm): Do we need to handle this case? |
| 1335 return null; |
| 1336 } |
| 1337 |
| 1338 String name = node.name.name; |
| 1339 var annotation = findAnnotation(node.element, isJsName); |
| 1340 if (annotation != null) { |
| 1341 name = getConstantField(annotation, 'name', types.stringType) |
| 1342 ?.toStringValue(); |
| 1343 } |
| 1344 if (node.isGetter) { |
| 1345 return new JS.Fun(params, js.statement('{ return this.#; }', [name])); |
| 1346 } else if (node.isSetter) { |
| 1347 return new JS.Fun( |
| 1348 params, js.statement('{ this.# = #; }', [name, params.last])); |
| 1349 } else { |
| 1350 return new JS.Fun( |
| 1351 params, js.statement('{ return this.#(#); }', [name, params])); |
| 1352 } |
| 1353 } |
| 1354 |
1325 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { | 1355 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { |
1326 if (node.isAbstract || _externalOrNative(node)) { | 1356 if (node.isAbstract) { |
1327 return null; | 1357 return null; |
1328 } | 1358 } |
1329 | 1359 |
1330 var params = _visit(node.parameters) as List<JS.Parameter>; | 1360 var params = _visit(node.parameters) as List<JS.Parameter>; |
1331 if (params == null) params = <JS.Parameter>[]; | 1361 if (params == null) params = <JS.Parameter>[]; |
1332 | 1362 |
1333 var typeParams = _emitTypeParams(node.element).toList(); | 1363 JS.Fun fn; |
1334 var returnType = emitTypeRef(node.element.returnType); | 1364 if (_externalOrNative(node)) { |
1335 JS.Fun fn = _emitFunctionBody(params, node.body, typeParams, returnType); | 1365 fn = _emitNativeFunctionBody(params, node); |
| 1366 // TODO(vsm): Remove if / when we handle the static case above. |
| 1367 if (fn == null) return null; |
| 1368 } else { |
| 1369 var typeParams = _emitTypeParams(node.element).toList(); |
| 1370 var returnType = emitTypeRef(node.element.returnType); |
| 1371 fn = _emitFunctionBody(params, node.body, typeParams, returnType); |
| 1372 } |
| 1373 |
1336 if (node.operatorKeyword != null && | 1374 if (node.operatorKeyword != null && |
1337 node.name.name == '[]=' && | 1375 node.name.name == '[]=' && |
1338 params.isNotEmpty) { | 1376 params.isNotEmpty) { |
1339 // []= methods need to return the value. We could also address this at | 1377 // []= methods need to return the value. We could also address this at |
1340 // call sites, but it's cleaner to instead transform the operator method. | 1378 // call sites, but it's cleaner to instead transform the operator method. |
1341 var returnValue = new JS.Return(params.last); | 1379 var returnValue = new JS.Return(params.last); |
1342 var body = fn.body; | 1380 var body = fn.body; |
1343 if (JS.Return.foundIn(fn)) { | 1381 if (JS.Return.foundIn(fn)) { |
1344 // If a return is inside body, transform `(params) { body }` to | 1382 // If a return is inside body, transform `(params) { body }` to |
1345 // `(params) { (() => { body })(); return value; }`. | 1383 // `(params) { (() => { body })(); return value; }`. |
(...skipping 1578 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2924 return AstBuilder.methodInvoke( | 2962 return AstBuilder.methodInvoke( |
2925 newTarget, invoke.methodName, invoke.argumentList.arguments); | 2963 newTarget, invoke.methodName, invoke.argumentList.arguments); |
2926 } | 2964 } |
2927 } | 2965 } |
2928 | 2966 |
2929 bool _requiresStaticDispatch(Expression target, String memberName) { | 2967 bool _requiresStaticDispatch(Expression target, String memberName) { |
2930 var type = getStaticType(target); | 2968 var type = getStaticType(target); |
2931 if (!_isObjectProperty(memberName)) { | 2969 if (!_isObjectProperty(memberName)) { |
2932 return false; | 2970 return false; |
2933 } | 2971 } |
2934 if (!type.isObject && !_isJSBuiltinType(type) && !_isNullable(target)) { | 2972 if (!type.isObject && |
| 2973 !_isJSBuiltinType(type) && |
| 2974 !_extensionTypes.contains(type.element) && |
| 2975 !_isNullable(target)) { |
2935 return false; | 2976 return false; |
2936 } | 2977 } |
2937 return true; | 2978 return true; |
2938 } | 2979 } |
2939 | 2980 |
2940 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 2981 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
2941 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { | 2982 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { |
2942 var member = memberId.staticElement; | 2983 var member = memberId.staticElement; |
2943 if (member is PropertyAccessorElement) { | 2984 if (member is PropertyAccessorElement) { |
2944 member = (member as PropertyAccessorElement).variable; | 2985 member = (member as PropertyAccessorElement).variable; |
(...skipping 673 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3618 } else if (type is InterfaceType && type.element == expectedType.element) { | 3659 } else if (type is InterfaceType && type.element == expectedType.element) { |
3619 return type.typeArguments[0]; | 3660 return type.typeArguments[0]; |
3620 } else { | 3661 } else { |
3621 // TODO(leafp): The above only handles the case where the return type | 3662 // TODO(leafp): The above only handles the case where the return type |
3622 // is exactly Future/Stream/Iterable. Handle the subtype case. | 3663 // is exactly Future/Stream/Iterable. Handle the subtype case. |
3623 return DynamicTypeImpl.instance; | 3664 return DynamicTypeImpl.instance; |
3624 } | 3665 } |
3625 } | 3666 } |
3626 } | 3667 } |
3627 | 3668 |
3628 class JSGenerator extends CodeGenerator { | 3669 class _ExtensionFinder extends GeneralizingElementVisitor { |
3629 final _extensionTypes = new HashSet<ClassElement>(); | 3670 final AnalysisContext _context; |
| 3671 final HashSet<ClassElement> _extensionTypes; |
3630 final TypeProvider _types; | 3672 final TypeProvider _types; |
3631 JSGenerator(AbstractCompiler compiler) | 3673 |
3632 : _types = compiler.context.typeProvider, | 3674 _ExtensionFinder(this._context, this._extensionTypes, this._types); |
3633 super(compiler) { | 3675 |
3634 // TODO(jacobr): determine the the set of types with extension methods from | 3676 visitClassElement(ClassElement element) { |
3635 // the annotations rather than hard coding the list once the analyzer | 3677 if (findAnnotation(element, isJsPeerInterface) != null || |
3636 // supports summaries. | 3678 findAnnotation(element, isNativeAnnotation) != null) { |
3637 var context = compiler.context; | 3679 _addExtensionType(element.type); |
3638 var src = context.sourceFactory.forUri('dart:_interceptors'); | |
3639 var interceptors = context.computeLibraryElement(src); | |
3640 for (var t in ['JSArray', 'JSString', 'JSNumber', 'JSBool']) { | |
3641 _addExtensionType(interceptors.getType(t).type); | |
3642 } | 3680 } |
3643 // TODO(jmesserly): manually add `int` and `double` | |
3644 // Unfortunately our current analyzer rejects "implements int". | |
3645 // Fix was landed, so we can remove this hack once we're updated: | |
3646 // https://github.com/dart-lang/sdk/commit/d7cd11f86a02f55269fc8d9843e7758eb
eeb81c8 | |
3647 _addExtensionType(_types.intType); | |
3648 _addExtensionType(_types.doubleType); | |
3649 } | 3681 } |
3650 | 3682 |
3651 void _addExtensionType(InterfaceType t) { | 3683 void _addExtensionType(InterfaceType t) { |
3652 if (t.isObject || !_extensionTypes.add(t.element)) return; | 3684 if (t.isObject || !_extensionTypes.add(t.element)) return; |
3653 t = fillDynamicTypeArgs(t, _types) as InterfaceType; | 3685 t = fillDynamicTypeArgs(t, _types) as InterfaceType; |
3654 t.interfaces.forEach(_addExtensionType); | 3686 t.interfaces.forEach(_addExtensionType); |
3655 t.mixins.forEach(_addExtensionType); | 3687 t.mixins.forEach(_addExtensionType); |
3656 _addExtensionType(t.superclass); | 3688 _addExtensionType(t.superclass); |
3657 } | 3689 } |
3658 | 3690 |
| 3691 void _addExtensionTypes(String libraryUri) { |
| 3692 var sourceFactory = _context.sourceFactory.forUri(libraryUri); |
| 3693 var library = _context.computeLibraryElement(sourceFactory); |
| 3694 visitLibraryElement(library); |
| 3695 } |
| 3696 } |
| 3697 |
| 3698 class JSGenerator extends CodeGenerator { |
| 3699 final _extensionTypes = new HashSet<ClassElement>(); |
| 3700 final TypeProvider _types; |
| 3701 |
| 3702 JSGenerator(AbstractCompiler compiler) |
| 3703 : _types = compiler.context.typeProvider, |
| 3704 super(compiler) { |
| 3705 // TODO(vsm): Eventually, we want to make this extensible - i.e., find |
| 3706 // annotations in user code as well. It would need to be summarized in |
| 3707 // the element model - not searched this way on every compile. |
| 3708 var finder = new _ExtensionFinder(context, _extensionTypes, _types); |
| 3709 finder._addExtensionTypes('dart:_interceptors'); |
| 3710 finder._addExtensionTypes('dart:_native_typed_data'); |
| 3711 |
| 3712 // TODO(vsm): If we're analyzing against the main SDK, those |
| 3713 // types are not explicitly annotated. |
| 3714 finder._addExtensionType(_types.intType); |
| 3715 finder._addExtensionType(_types.doubleType); |
| 3716 finder._addExtensionType(_types.boolType); |
| 3717 finder._addExtensionType(_types.stringType); |
| 3718 } |
| 3719 |
3659 String generateLibrary(LibraryUnit unit) { | 3720 String generateLibrary(LibraryUnit unit) { |
3660 // Clone the AST first, so we can mutate it. | 3721 // Clone the AST first, so we can mutate it. |
3661 unit = unit.clone(); | 3722 unit = unit.clone(); |
3662 var library = unit.library.element.library; | 3723 var library = unit.library.element.library; |
3663 var fields = findFieldsNeedingStorage(unit, _extensionTypes); | 3724 var fields = findFieldsNeedingStorage(unit, _extensionTypes); |
3664 var rules = new StrongTypeSystemImpl(); | 3725 var rules = new StrongTypeSystemImpl(); |
3665 var codegen = | 3726 var codegen = |
3666 new JSCodegenVisitor(compiler, rules, library, _extensionTypes, fields); | 3727 new JSCodegenVisitor(compiler, rules, library, _extensionTypes, fields); |
3667 var module = codegen.emitLibrary(unit); | 3728 var module = codegen.emitLibrary(unit); |
3668 var out = compiler.getOutputPath(library.source.uri); | 3729 var out = compiler.getOutputPath(library.source.uri); |
(...skipping 25 matching lines...) Expand all Loading... |
3694 | 3755 |
3695 /// A special kind of element created by the compiler, signifying a temporary | 3756 /// A special kind of element created by the compiler, signifying a temporary |
3696 /// variable. These objects use instance equality, and should be shared | 3757 /// variable. These objects use instance equality, and should be shared |
3697 /// everywhere in the tree where they are treated as the same variable. | 3758 /// everywhere in the tree where they are treated as the same variable. |
3698 class TemporaryVariableElement extends LocalVariableElementImpl { | 3759 class TemporaryVariableElement extends LocalVariableElementImpl { |
3699 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3760 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3700 | 3761 |
3701 int get hashCode => identityHashCode(this); | 3762 int get hashCode => identityHashCode(this); |
3702 bool operator ==(Object other) => identical(this, other); | 3763 bool operator ==(Object other) => identical(this, other); |
3703 } | 3764 } |
OLD | NEW |