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 'package:analyzer/analyzer.dart' show ParameterKind; | 5 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
6 import 'package:analyzer/src/generated/element.dart'; | 6 import 'package:analyzer/src/generated/element.dart'; |
7 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 7 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
8 | 8 |
| 9 import '../js/js_ast.dart' as JS show Node, Expression, TypeRef; |
| 10 |
9 import 'closure_annotation.dart'; | 11 import 'closure_annotation.dart'; |
10 import 'closure_type.dart'; | |
11 | 12 |
12 /// Mixin that can generate [ClosureAnnotation]s for Dart elements and types. | 13 /// Mixin that can generate [ClosureAnnotation]s for Dart elements and types. |
13 abstract class ClosureAnnotator { | 14 abstract class ClosureAnnotator { |
14 TypeProvider get types; | 15 TypeProvider get types; |
15 | 16 |
16 /// Must return a JavaScript qualified name that can be used to refer to [type
]. | 17 JS.TypeRef emitTypeRef(DartType type); |
17 String getQualifiedName(TypeDefiningElement type); | |
18 | |
19 ClosureAnnotation closureAnnotationForVariable(VariableElement e) => | |
20 new ClosureAnnotation( | |
21 type: _closureTypeForDartType(e.type), | |
22 // Note: we don't set isConst here because Closure's constness and | |
23 // Dart's are not really compatible. | |
24 isFinal: e.isFinal || e.isConst); | |
25 | |
26 /// We don't use Closure's `@typedef` annotations | |
27 ClosureAnnotation closureAnnotationForTypeDef(FunctionTypeAliasElement e) => | |
28 new ClosureAnnotation( | |
29 type: _closureTypeForDartType(e.type, forceTypeDefExpansion: true), | |
30 isTypedef: true); | |
31 | |
32 ClosureAnnotation closureAnnotationForDefaultConstructor(ClassElement e) => | |
33 new ClosureAnnotation( | |
34 superType: _closureTypeForDartType(e.supertype), | |
35 interfaces: e.interfaces.map(_closureTypeForDartType).toList()); | |
36 | 18 |
37 // TODO(ochafik): Handle destructured params when Closure supports it. | 19 // TODO(ochafik): Handle destructured params when Closure supports it. |
38 ClosureAnnotation closureAnnotationFor( | 20 ClosureAnnotation closureAnnotationFor( |
39 ExecutableElement e, String namedArgsMapName) { | 21 JS.Node node, AstNode original, Element e, String namedArgsMapName) { |
40 var paramTypes = <String, ClosureType>{}; | 22 String comment; |
41 var namedArgs = <String, ClosureType>{}; | 23 if (original is AnnotatedNode && original.documentationComment != null) { |
42 for (var param in e.parameters) { | 24 comment = original.documentationComment.toSource(); |
43 var t = _closureTypeForDartType(param.type); | |
44 switch (param.parameterKind) { | |
45 case ParameterKind.NAMED: | |
46 namedArgs[param.name] = t.orUndefined(); | |
47 break; | |
48 case ParameterKind.POSITIONAL: | |
49 paramTypes[param.name] = t.toOptional(); | |
50 break; | |
51 case ParameterKind.REQUIRED: | |
52 paramTypes[param.name] = t; | |
53 break; | |
54 } | |
55 } | 25 } |
56 if (namedArgs.isNotEmpty) { | 26 // Note: Dart and Closure privacy are not compatible: don't set `isPrivate:
e.isPrivate`. |
57 paramTypes[namedArgsMapName] = | |
58 new ClosureType.record(namedArgs).toOptional(); | |
59 } | |
60 | |
61 var returnType = e is ConstructorElement | |
62 ? (e.isFactory ? _closureTypeForClass(e.enclosingElement) : null) | |
63 : _closureTypeForDartType(e.returnType); | |
64 | |
65 return new ClosureAnnotation( | 27 return new ClosureAnnotation( |
| 28 comment: comment, |
| 29 // Note: we don't set isConst here because Closure's constness and |
| 30 // Dart's are not really compatible. |
| 31 isFinal: e is VariableElement && (e.isFinal || e.isConst), |
| 32 type: e is VariableElement |
| 33 ? emitTypeRef(e.type /*, forceTypeDefExpansion: true*/) |
| 34 : null, |
| 35 superType: e is ClassElement ? emitTypeRef(e.supertype) : null, |
| 36 interfaces: |
| 37 e is ClassElement ? e.interfaces.map(emitTypeRef).toList() : null, |
66 isOverride: e.isOverride, | 38 isOverride: e.isOverride, |
67 // Note: Dart and Closure privacy are not compatible: don't set `isPriva
te: e.isPrivate`. | 39 isTypedef: e is FunctionTypeAliasElement); |
68 paramTypes: paramTypes, | |
69 returnType: returnType); | |
70 } | 40 } |
71 | |
72 Map<DartType, ClosureType> __commonTypes; | |
73 Map<DartType, ClosureType> get _commonTypes { | |
74 if (__commonTypes == null) { | |
75 var numberType = new ClosureType.number().toNullable(); | |
76 __commonTypes = { | |
77 types.intType: numberType, | |
78 types.numType: numberType, | |
79 types.doubleType: numberType, | |
80 types.boolType: new ClosureType.boolean().toNullable(), | |
81 types.stringType: new ClosureType.string(), | |
82 }; | |
83 } | |
84 return __commonTypes; | |
85 } | |
86 | |
87 ClosureType _closureTypeForClass(ClassElement classElement, [DartType type]) { | |
88 ClosureType closureType = _commonTypes[type]; | |
89 if (closureType != null) return closureType; | |
90 | |
91 var fullName = _getFullName(classElement); | |
92 switch (fullName) { | |
93 // TODO(ochafik): Test DartTypes directly if possible. | |
94 case "dart.js.JsArray": | |
95 return new ClosureType.array( | |
96 type is InterfaceType && type.typeArguments.length == 1 | |
97 ? _closureTypeForDartType(type.typeArguments.single) | |
98 : null); | |
99 case "dart.js.JsObject": | |
100 return new ClosureType.map(); | |
101 case "dart.js.JsFunction": | |
102 return new ClosureType.function(); | |
103 default: | |
104 return new ClosureType.type(getQualifiedName(classElement)); | |
105 } | |
106 } | |
107 | |
108 ClosureType _closureTypeForDartType(DartType type, | |
109 {bool forceTypeDefExpansion: false}) { | |
110 if (type == null || type.isBottom || type.isDynamic) { | |
111 return new ClosureType.unknown(); | |
112 } | |
113 if (type.isVoid) return null; | |
114 if (type is FunctionType) { | |
115 if (!forceTypeDefExpansion && type.element.name != '') { | |
116 return new ClosureType.type(getQualifiedName(type.element)); | |
117 } | |
118 | |
119 var args = [] | |
120 ..addAll(type.normalParameterTypes.map(_closureTypeForDartType)) | |
121 ..addAll(type.optionalParameterTypes | |
122 .map((t) => _closureTypeForDartType(t).toOptional())); | |
123 if (type.namedParameterTypes.isNotEmpty) { | |
124 var namedArgs = <String, ClosureType>{}; | |
125 type.namedParameterTypes.forEach((n, t) { | |
126 namedArgs[n] = _closureTypeForDartType(t).orUndefined(); | |
127 }); | |
128 args.add(new ClosureType.record(namedArgs).toOptional()); | |
129 } | |
130 return new ClosureType.function( | |
131 args, _closureTypeForDartType(type.returnType)); | |
132 } | |
133 if (type is InterfaceType) { | |
134 return _closureTypeForClass(type.element, type); | |
135 } | |
136 return new ClosureType.unknown(); | |
137 } | |
138 | |
139 /// TODO(ochafik): Use a package-and-file-uri-dependent naming, since librarie
s can collide. | |
140 String _getFullName(ClassElement type) => | |
141 type.library.name == '' ? type.name : '${type.library.name}.${type.name}'; | |
142 } | 41 } |
OLD | NEW |