1
+ // Copyright (c) 2012, 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 closureAnnotationForDefaultConstructor (ClassElement e) {
27
+ return new ClosureAnnotation (
28
+ isConstructor: ! generatesES6Classes,
29
+ isStruct: ! generatesES6Classes,
30
+ superType: _closureTypeForDartType (e.supertype),
31
+ interfaces: e.interfaces.map (_closureTypeForDartType).toList ()
32
+ );
33
+ }
34
+
35
+ ClosureAnnotation closureAnnotationFor (ExecutableElement e, String namedArgsMapName) {
36
+ var paramTypes = < String , ClosureType > {};
37
+ var namedArgs = < String , ClosureType > {};
38
+ for (var param in e.parameters) {
39
+ var t = _closureTypeForDartType (param.type);
40
+ switch (param.parameterKind) {
41
+ case ParameterKind .NAMED :
42
+ namedArgs[param.name] = t.orUndefined ();
43
+ break ;
44
+ case ParameterKind .POSITIONAL :
45
+ paramTypes[param.name] = t.toOptional ();
46
+ break ;
47
+ case ParameterKind .REQUIRED :
48
+ paramTypes[param.name] = t;
49
+ break ;
50
+ }
51
+ }
52
+ if (namedArgs.isNotEmpty) {
53
+ paramTypes[namedArgsMapName] = new ClosureType .record (namedArgs).toOptional ();
54
+ }
55
+
56
+ bool isConstructor = e is ConstructorElement && ! e.isFactory && ! generatesES6Classes;
57
+ bool isFactory = e is ConstructorElement && e.isFactory;
58
+ enclosingType () => _closureTypeForClass (e.enclosingElement);
59
+
60
+ return new ClosureAnnotation (
61
+ isConstructor: isConstructor,
62
+ isStruct: isConstructor,
63
+ isOverride: e.isOverride,
64
+ thisType: e.enclosingElement is ClassElement && ! e.isStatic
65
+ && ! generatesES6Classes ?
66
+ enclosingType () : null ,
67
+ // Note: Dart and Closure privacy are not compatible: don't set `isPrivate: e.isPrivate`.
68
+ paramTypes: paramTypes,
69
+ returnType: isFactory ? enclosingType () : _closureTypeForDartType (e.returnType)
70
+ );
71
+ }
72
+
73
+ Map <DartType , ClosureType > __commonTypes;
74
+ Map <DartType , ClosureType > get _commonTypes {
75
+ if (__commonTypes == null ) {
76
+ var numberType = new ClosureType .number ().toNullable ();
77
+ __commonTypes = {
78
+ types.intType: numberType,
79
+ types.numType: numberType,
80
+ types.doubleType: numberType,
81
+ types.boolType: new ClosureType .boolean ().toNullable (),
82
+ types.stringType: new ClosureType .string (),
83
+ };
84
+ }
85
+ return __commonTypes;
86
+ }
87
+
88
+ ClosureType _closureTypeForClass (ClassElement classElement, [DartType type]) {
89
+ ClosureType closureType = _commonTypes[type];
90
+ if (closureType != null ) return closureType;
91
+
92
+ var fullName = _getFullName (classElement);
93
+ switch (fullName) {
94
+ // TODO(ochafik): Test DartTypes directly if possible.
95
+ case "dart.js.JsArray" :
96
+ return new ClosureType .array (
97
+ type is InterfaceType && type.typeArguments.length == 1
98
+ ? _closureTypeForDartType (type.typeArguments.single) : 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
+ // TODO(ochafik): Work out the practical difference between all (*) and unknown (?).
110
+ if (type == null ) return new ClosureType .unknown ();
111
+ if (type.isDynamic) return new ClosureType .all ();
112
+ if (type.isVoid) return null ;
113
+ if (type is FunctionType ) {
114
+ var args = []
115
+ ..addAll (type.normalParameterTypes.map (_closureTypeForDartType))
116
+ ..addAll (type.optionalParameterTypes.map ((t) => _closureTypeForDartType (t).toOptional ()));
117
+ if (type.namedParameterTypes.isNotEmpty) {
118
+ var namedArgs = < String , ClosureType > {};
119
+ type.namedParameterTypes.forEach ((n, t) {
120
+ namedArgs[n] = _closureTypeForDartType (t).orUndefined ();
121
+ });
122
+ args.add (new ClosureType .record (namedArgs).toOptional ());
123
+ }
124
+ return new ClosureType .function (args, _closureTypeForDartType (type.returnType));
125
+ }
126
+ if (type is InterfaceType ) {
127
+ return _closureTypeForClass (type.element, type);
128
+ }
129
+ return new ClosureType .all ();
130
+ }
131
+
132
+ /// TODO(ochafik): Use a package-and-file-uri-dependent naming, since libraries can collide.
133
+ String _getFullName (ClassElement type) =>
134
+ type.library.name == '' ? type.name : '${type .library .name }.${type .name }' ;
135
+ }
0 commit comments