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
3
+ // BSD-style license that can be found in the LICENSE file.
4
+
5
+ library dev_compiler.src.closure.closure_codegen;
6
+
7
+ import 'package:analyzer/analyzer.dart' show ParameterKind;
8
+ import 'package:analyzer/src/generated/element.dart' ;
9
+
10
+ import 'closure_annotation.dart' ;
11
+ import 'closure_type.dart' ;
12
+ import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
13
+
14
+ /// Mixin that can generate [ClosureAnnotation] s for Dart elements and types.
15
+ abstract class ClosureCodegen {
16
+
17
+ TypeProvider get types;
18
+
19
+ /// Must return a JavaScript qualified name that can be used to refer to [type] .
20
+ String getQualifiedName (ClassElement type);
21
+
22
+ /// Closure treats ES6 classes as @struct by default.
23
+ /// Also, no need to declare @this on their instance members.
24
+ bool get generatesES6Classes => true ;
25
+
26
+ ClosureAnnotation closureAnnotationForVariable (VariableElement e) {
27
+ return new ClosureAnnotation (
28
+ type: _closureTypeForDartType (e.type),
29
+ // Note: we don't set isConst here because Closure's constness and
30
+ // Dart's are not really compatible.
31
+ isFinal: e.isFinal || e.isConst);
32
+ }
33
+
34
+ ClosureAnnotation closureAnnotationForDefaultConstructor (ClassElement e) {
35
+ return new ClosureAnnotation (
36
+ isConstructor: ! generatesES6Classes,
37
+ isStruct: ! generatesES6Classes,
38
+ superType: _closureTypeForDartType (e.supertype),
39
+ interfaces: e.interfaces.map (_closureTypeForDartType).toList ());
40
+ }
41
+
42
+ ClosureAnnotation closureAnnotationFor (ExecutableElement e, String namedArgsMapName) {
43
+ var paramTypes = < String , ClosureType > {};
44
+ var namedArgs = < String , ClosureType > {};
45
+ for (var param in e.parameters) {
46
+ var t = _closureTypeForDartType (param.type);
47
+ switch (param.parameterKind) {
48
+ case ParameterKind .NAMED :
49
+ namedArgs[param.name] = t.orUndefined ();
50
+ break ;
51
+ case ParameterKind .POSITIONAL :
52
+ paramTypes[param.name] = t.toOptional ();
53
+ break ;
54
+ case ParameterKind .REQUIRED :
55
+ paramTypes[param.name] = t;
56
+ break ;
57
+ }
58
+ }
59
+ if (namedArgs.isNotEmpty) {
60
+ paramTypes[namedArgsMapName] = new ClosureType .record (namedArgs).toOptional ();
61
+ }
62
+
63
+ bool isConstructor = e is ConstructorElement && ! e.isFactory && ! generatesES6Classes;
64
+ bool isFactory = e is ConstructorElement && e.isFactory;
65
+ enclosingType () => _closureTypeForClass (e.enclosingElement);
66
+
67
+ var returnType =
68
+ isFactory ? enclosingType () : _closureTypeForDartType (e.returnType);
69
+
70
+ return new ClosureAnnotation (
71
+ isConstructor: isConstructor,
72
+ isStruct: isConstructor,
73
+ isOverride: e.isOverride,
74
+ thisType: e.enclosingElement is ClassElement && ! e.isStatic
75
+ && ! generatesES6Classes ?
76
+ enclosingType () : null ,
77
+ // Note: Dart and Closure privacy are not compatible: don't set `isPrivate: e.isPrivate`.
78
+ paramTypes: paramTypes,
79
+ returnType: returnType);
80
+ }
81
+
82
+ Map <DartType , ClosureType > __commonTypes;
83
+ Map <DartType , ClosureType > get _commonTypes {
84
+ if (__commonTypes == null ) {
85
+ var numberType = new ClosureType .number ().toNullable ();
86
+ __commonTypes = {
87
+ types.intType: numberType,
88
+ types.numType: numberType,
89
+ types.doubleType: numberType,
90
+ types.boolType: new ClosureType .boolean ().toNullable (),
91
+ types.stringType: new ClosureType .string (),
92
+ };
93
+ }
94
+ return __commonTypes;
95
+ }
96
+
97
+ ClosureType _closureTypeForClass (ClassElement classElement, [DartType type]) {
98
+ ClosureType closureType = _commonTypes[type];
99
+ if (closureType != null ) return closureType;
100
+
101
+ var fullName = _getFullName (classElement);
102
+ switch (fullName) {
103
+ // TODO(ochafik): Test DartTypes directly if possible.
104
+ case "dart.js.JsArray" :
105
+ return new ClosureType .array (
106
+ type is InterfaceType && type.typeArguments.length == 1
107
+ ? _closureTypeForDartType (type.typeArguments.single) : null );
108
+ case "dart.js.JsObject" :
109
+ return new ClosureType .map ();
110
+ case "dart.js.JsFunction" :
111
+ return new ClosureType .function ();
112
+ default :
113
+ return new ClosureType .type (getQualifiedName (classElement));
114
+ }
115
+ }
116
+
117
+ ClosureType _closureTypeForDartType (DartType type) {
118
+ // TODO(ochafik): Work out the practical difference between all (*) and unknown (?).
119
+ if (type == null ) return new ClosureType .unknown ();
120
+ if (type.isDynamic) return new ClosureType .all ();
121
+ if (type.isVoid) return null ;
122
+ if (type is FunctionType ) {
123
+ var args = []
124
+ ..addAll (type.normalParameterTypes.map (_closureTypeForDartType))
125
+ ..addAll (type.optionalParameterTypes.map ((t) => _closureTypeForDartType (t).toOptional ()));
126
+ if (type.namedParameterTypes.isNotEmpty) {
127
+ var namedArgs = < String , ClosureType > {};
128
+ type.namedParameterTypes.forEach ((n, t) {
129
+ namedArgs[n] = _closureTypeForDartType (t).orUndefined ();
130
+ });
131
+ args.add (new ClosureType .record (namedArgs).toOptional ());
132
+ }
133
+ return new ClosureType .function (args, _closureTypeForDartType (type.returnType));
134
+ }
135
+ if (type is InterfaceType ) {
136
+ return _closureTypeForClass (type.element, type);
137
+ }
138
+ return new ClosureType .all ();
139
+ }
140
+
141
+ /// TODO(ochafik): Use a package-and-file-uri-dependent naming, since libraries can collide.
142
+ String _getFullName (ClassElement type) =>
143
+ type.library.name == '' ? type.name : '${type .library .name }.${type .name }' ;
144
+ }
0 commit comments