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 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; | 5 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; |
6 | 6 |
7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
8 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 8 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
9 import 'package:analyzer/src/generated/constant.dart'; | 9 import 'package:analyzer/src/generated/constant.dart'; |
10 import 'package:analyzer/src/generated/element.dart'; | 10 import 'package:analyzer/src/generated/element.dart'; |
11 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; | |
11 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 12 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
12 import 'package:analyzer/src/generated/scanner.dart' | 13 import 'package:analyzer/src/generated/scanner.dart' |
13 show StringToken, Token, TokenType; | 14 show StringToken, Token, TokenType; |
14 import 'package:analyzer/src/generated/type_system.dart' | 15 import 'package:analyzer/src/generated/type_system.dart' |
15 show StrongTypeSystemImpl; | 16 show StrongTypeSystemImpl; |
16 import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder; | 17 import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder; |
17 | 18 |
18 import 'ast_builder.dart' show AstBuilder; | 19 import 'ast_builder.dart' show AstBuilder; |
19 import 'reify_coercions.dart' show CoercionReifier, Tuple2; | 20 import 'reify_coercions.dart' show CoercionReifier, Tuple2; |
20 | 21 |
(...skipping 400 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
421 } else if (member is MethodDeclaration) { | 422 } else if (member is MethodDeclaration) { |
422 methods.add(member); | 423 methods.add(member); |
423 } | 424 } |
424 } | 425 } |
425 | 426 |
426 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), | 427 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), |
427 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); | 428 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); |
428 | 429 |
429 String jsPeerName; | 430 String jsPeerName; |
430 var jsPeer = findAnnotation(classElem, isJsPeerInterface); | 431 var jsPeer = findAnnotation(classElem, isJsPeerInterface); |
432 // Only look at "Native" annotations on registered extension types. | |
433 // E.g., we're current ignoring the ones in dart:html. | |
434 if (jsPeer == null && _extensionTypes.contains(classElem)) { | |
435 jsPeer = findAnnotation(classElem, isNativeAnnotation); | |
436 } | |
431 if (jsPeer != null) { | 437 if (jsPeer != null) { |
432 jsPeerName = | 438 jsPeerName = |
433 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue(); | 439 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue(); |
440 if (jsPeerName.contains(',')) { | |
441 jsPeerName = jsPeerName.split(',')[0]; | |
442 } | |
434 } | 443 } |
435 | 444 |
436 var body = _finishClassMembers(classElem, classExpr, ctors, fields, | 445 var body = _finishClassMembers(classElem, classExpr, ctors, fields, |
437 staticFields, methods, node.metadata, jsPeerName); | 446 staticFields, methods, node.metadata, jsPeerName); |
438 | 447 |
439 var result = _finishClassDef(type, body); | 448 var result = _finishClassDef(type, body); |
440 | 449 |
441 if (jsPeerName != null) { | 450 if (jsPeerName != null) { |
442 // This class isn't allowed to be lazy, because we need to set up | 451 // This class isn't allowed to be lazy, because we need to set up |
443 // the native JS type eagerly at this point. | 452 // the native JS type eagerly at this point. |
(...skipping 823 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1267 | 1276 |
1268 JS.Expression _defaultParamValue(FormalParameter param) { | 1277 JS.Expression _defaultParamValue(FormalParameter param) { |
1269 if (param is DefaultFormalParameter && param.defaultValue != null) { | 1278 if (param is DefaultFormalParameter && param.defaultValue != null) { |
1270 return _visit(param.defaultValue); | 1279 return _visit(param.defaultValue); |
1271 } else { | 1280 } else { |
1272 return new JS.LiteralNull(); | 1281 return new JS.LiteralNull(); |
1273 } | 1282 } |
1274 } | 1283 } |
1275 | 1284 |
1276 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { | 1285 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { |
1277 if (node.isAbstract || _externalOrNative(node)) { | 1286 if (node.isAbstract) { |
1278 return null; | 1287 return null; |
1279 } | 1288 } |
1280 | 1289 |
1281 var params = _visit(node.parameters) as List<JS.Parameter>; | 1290 var params = _visit(node.parameters) as List<JS.Parameter>; |
1282 if (params == null) params = <JS.Parameter>[]; | 1291 if (params == null) params = <JS.Parameter>[]; |
1283 | 1292 |
1284 JS.Fun fn = _emitFunctionBody(params, node.body); | 1293 JS.Fun fn; |
1294 if (_externalOrNative(node)) { | |
1295 if (node.isStatic) { | |
Jennifer Messerly
2016/02/10 00:20:30
Perhaps factor this out into _emitNativeFunctionBo
vsm
2016/02/11 22:36:06
Done.
| |
1296 // TODO(vsm): Do we need to handle this case? | |
1297 return null; | |
1298 } | |
1299 | |
1300 String name = node.name.name; | |
1301 var annotation = findAnnotation(node.element, isJsName); | |
1302 if (annotation != null) { | |
1303 name = getConstantField(annotation, 'name', types.stringType) | |
1304 ?.toStringValue(); | |
1305 } | |
1306 if (node.isGetter) { | |
1307 fn = new JS.Fun(params, js.statement('{ return this.#; }', [name])); | |
1308 } else if (node.isSetter) { | |
1309 fn = new JS.Fun( | |
1310 params, js.statement('{ this.# = #; }', [name, params.last])); | |
1311 } else { | |
1312 fn = new JS.Fun( | |
1313 params, js.statement('{ return this.#(#); }', [name, params])); | |
1314 } | |
1315 } else { | |
1316 fn = _emitFunctionBody(params, node.body); | |
1317 } | |
1318 | |
1285 if (node.operatorKeyword != null && | 1319 if (node.operatorKeyword != null && |
1286 node.name.name == '[]=' && | 1320 node.name.name == '[]=' && |
1287 params.isNotEmpty) { | 1321 params.isNotEmpty) { |
1288 // []= methods need to return the value. We could also address this at | 1322 // []= methods need to return the value. We could also address this at |
1289 // call sites, but it's cleaner to instead transform the operator method. | 1323 // call sites, but it's cleaner to instead transform the operator method. |
1290 var returnValue = new JS.Return(params.last); | 1324 var returnValue = new JS.Return(params.last); |
1291 var body = fn.body; | 1325 var body = fn.body; |
1292 if (JS.Return.foundIn(fn)) { | 1326 if (JS.Return.foundIn(fn)) { |
1293 // If a return is inside body, transform `(params) { body }` to | 1327 // If a return is inside body, transform `(params) { body }` to |
1294 // `(params) { (() => { body })(); return value; }`. | 1328 // `(params) { (() => { body })(); return value; }`. |
(...skipping 2253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3548 } else if (type is InterfaceType && type.element == expectedType.element) { | 3582 } else if (type is InterfaceType && type.element == expectedType.element) { |
3549 return type.typeArguments[0]; | 3583 return type.typeArguments[0]; |
3550 } else { | 3584 } else { |
3551 // TODO(leafp): The above only handles the case where the return type | 3585 // TODO(leafp): The above only handles the case where the return type |
3552 // is exactly Future/Stream/Iterable. Handle the subtype case. | 3586 // is exactly Future/Stream/Iterable. Handle the subtype case. |
3553 return DynamicTypeImpl.instance; | 3587 return DynamicTypeImpl.instance; |
3554 } | 3588 } |
3555 } | 3589 } |
3556 } | 3590 } |
3557 | 3591 |
3558 class JSGenerator extends CodeGenerator { | 3592 class _ExtensionFinder extends GeneralizingElementVisitor { |
3559 final _extensionTypes = new HashSet<ClassElement>(); | 3593 final AnalysisContext _context; |
3594 final HashSet<ClassElement> _extensionTypes; | |
3560 final TypeProvider _types; | 3595 final TypeProvider _types; |
3561 JSGenerator(AbstractCompiler compiler) | 3596 |
3562 : _types = compiler.context.typeProvider, | 3597 _ExtensionFinder(this._context, this._extensionTypes, this._types); |
3563 super(compiler) { | 3598 |
3564 // TODO(jacobr): determine the the set of types with extension methods from | 3599 visitClassElement(ClassElement element) { |
3565 // the annotations rather than hard coding the list once the analyzer | 3600 if (findAnnotation(element, isJsPeerInterface) != null || |
3566 // supports summaries. | 3601 findAnnotation(element, isNativeAnnotation) != null) { |
3567 var context = compiler.context; | 3602 _addExtensionType(element.type); |
3568 var src = context.sourceFactory.forUri('dart:_interceptors'); | |
3569 var interceptors = context.computeLibraryElement(src); | |
3570 for (var t in ['JSArray', 'JSString', 'JSNumber', 'JSBool']) { | |
3571 _addExtensionType(interceptors.getType(t).type); | |
3572 } | 3603 } |
3573 // TODO(jmesserly): manually add `int` and `double` | |
3574 // Unfortunately our current analyzer rejects "implements int". | |
3575 // Fix was landed, so we can remove this hack once we're updated: | |
3576 // https://github.com/dart-lang/sdk/commit/d7cd11f86a02f55269fc8d9843e7758eb eeb81c8 | |
3577 _addExtensionType(_types.intType); | |
3578 _addExtensionType(_types.doubleType); | |
3579 } | 3604 } |
3580 | 3605 |
3581 void _addExtensionType(InterfaceType t) { | 3606 void _addExtensionType(InterfaceType t) { |
3582 if (t.isObject || !_extensionTypes.add(t.element)) return; | 3607 if (t.isObject || !_extensionTypes.add(t.element)) return; |
3583 t = fillDynamicTypeArgs(t, _types) as InterfaceType; | 3608 t = fillDynamicTypeArgs(t, _types) as InterfaceType; |
3584 t.interfaces.forEach(_addExtensionType); | 3609 t.interfaces.forEach(_addExtensionType); |
3585 t.mixins.forEach(_addExtensionType); | 3610 t.mixins.forEach(_addExtensionType); |
3586 _addExtensionType(t.superclass); | 3611 _addExtensionType(t.superclass); |
3587 } | 3612 } |
3588 | 3613 |
3614 void _addExtensionTypes(String libraryUri) { | |
3615 var sourceFactory = _context.sourceFactory.forUri(libraryUri); | |
3616 var library = _context.computeLibraryElement(sourceFactory); | |
3617 visitLibraryElement(library); | |
3618 } | |
3619 } | |
3620 | |
3621 class JSGenerator extends CodeGenerator { | |
3622 final _extensionTypes = new HashSet<ClassElement>(); | |
3623 final TypeProvider _types; | |
3624 | |
3625 JSGenerator(AbstractCompiler compiler) | |
3626 : _types = compiler.context.typeProvider, | |
3627 super(compiler) { | |
3628 // TODO(vsm): Eventually, we want to make this extensible - i.e., find | |
3629 // annotations in user code as well. It would need to be summarized in | |
3630 // the element model - not searched this way on every compile. | |
3631 var finder = new _ExtensionFinder(context, _extensionTypes, _types); | |
3632 finder._addExtensionTypes('dart:_interceptors'); | |
3633 finder._addExtensionTypes('dart:_native_typed_data'); | |
3634 | |
3635 // TODO(vsm): If we're analyzing against the main SDK, those | |
3636 // types are not explicitly annotated. | |
3637 finder._addExtensionType(_types.intType); | |
3638 finder._addExtensionType(_types.doubleType); | |
3639 finder._addExtensionType(_types.boolType); | |
3640 finder._addExtensionType(_types.stringType); | |
3641 } | |
3642 | |
3589 String generateLibrary(LibraryUnit unit) { | 3643 String generateLibrary(LibraryUnit unit) { |
3590 // Clone the AST first, so we can mutate it. | 3644 // Clone the AST first, so we can mutate it. |
3591 unit = unit.clone(); | 3645 unit = unit.clone(); |
3592 var library = unit.library.element.library; | 3646 var library = unit.library.element.library; |
3593 var fields = findFieldsNeedingStorage(unit, _extensionTypes); | 3647 var fields = findFieldsNeedingStorage(unit, _extensionTypes); |
3594 var rules = new StrongTypeSystemImpl(); | 3648 var rules = new StrongTypeSystemImpl(); |
3595 var codegen = | 3649 var codegen = |
3596 new JSCodegenVisitor(compiler, rules, library, _extensionTypes, fields); | 3650 new JSCodegenVisitor(compiler, rules, library, _extensionTypes, fields); |
3597 var module = codegen.emitLibrary(unit); | 3651 var module = codegen.emitLibrary(unit); |
3598 var out = compiler.getOutputPath(library.source.uri); | 3652 var out = compiler.getOutputPath(library.source.uri); |
(...skipping 25 matching lines...) Expand all Loading... | |
3624 | 3678 |
3625 /// A special kind of element created by the compiler, signifying a temporary | 3679 /// A special kind of element created by the compiler, signifying a temporary |
3626 /// variable. These objects use instance equality, and should be shared | 3680 /// variable. These objects use instance equality, and should be shared |
3627 /// everywhere in the tree where they are treated as the same variable. | 3681 /// everywhere in the tree where they are treated as the same variable. |
3628 class TemporaryVariableElement extends LocalVariableElementImpl { | 3682 class TemporaryVariableElement extends LocalVariableElementImpl { |
3629 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3683 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3630 | 3684 |
3631 int get hashCode => identityHashCode(this); | 3685 int get hashCode => identityHashCode(this); |
3632 bool operator ==(Object other) => identical(this, other); | 3686 bool operator ==(Object other) => identical(this, other); |
3633 } | 3687 } |
OLD | NEW |