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' as analyzer; | 5 import 'package:analyzer/analyzer.dart' as analyzer; |
6 import 'package:analyzer/src/generated/ast.dart'; | 6 import 'package:analyzer/src/generated/ast.dart'; |
7 import 'package:analyzer/src/generated/element.dart'; | 7 import 'package:analyzer/src/generated/element.dart'; |
8 import 'package:analyzer/src/generated/type_system.dart' | 8 import 'package:analyzer/src/generated/type_system.dart' |
9 show StrongTypeSystemImpl; | 9 show StrongTypeSystemImpl; |
10 import 'package:logging/logging.dart' as logger; | 10 import 'package:logging/logging.dart' as logger; |
11 | 11 |
12 import '../info.dart'; | 12 import '../info.dart'; |
13 | 13 |
14 import 'ast_builder.dart'; | 14 import 'ast_builder.dart'; |
15 | 15 |
16 final _log = new logger.Logger('dev_compiler.reify_coercions'); | 16 final _log = new logger.Logger('dev_compiler.reify_coercions'); |
17 | 17 |
18 // TODO(leafp) Factor this out or use an existing library | |
19 class Tuple2<T0, T1> { | |
20 final T0 e0; | |
21 final T1 e1; | |
22 Tuple2(this.e0, this.e1); | |
23 } | |
24 | |
25 typedef T Function1<S, T>(S _); | |
26 | |
27 class NewTypeIdDesc { | 18 class NewTypeIdDesc { |
28 /// If null, then this is not a library level identifier (i.e. it's | 19 /// If null, then this is not a library level identifier (i.e. it's |
29 /// a type parameter, or a special type like void, dynamic, etc) | 20 /// a type parameter, or a special type like void, dynamic, etc) |
30 LibraryElement importedFrom; | 21 LibraryElement importedFrom; |
31 | 22 |
32 /// True => use/def in same library | 23 /// True => use/def in same library |
33 bool fromCurrent; | 24 bool fromCurrent; |
34 | 25 |
35 /// True => not a source variable | 26 /// True => not a source variable |
36 bool synthetic; | 27 bool synthetic; |
37 NewTypeIdDesc({this.fromCurrent, this.importedFrom, this.synthetic}); | 28 NewTypeIdDesc({this.fromCurrent, this.importedFrom, this.synthetic}); |
38 } | 29 } |
39 | 30 |
40 // This class implements a pass which modifies (in place) the ast replacing | 31 // This class implements a pass which modifies (in place) the ast replacing |
41 // abstract coercion nodes with their dart implementations. | 32 // abstract coercion nodes with their dart implementations. |
42 class CoercionReifier extends analyzer.GeneralizingAstVisitor<Object> { | 33 class CoercionReifier extends analyzer.GeneralizingAstVisitor<Object> { |
43 final CoercionManager _cm; | |
44 final TypeManager _tm; | |
45 final VariableManager _vm; | |
46 final LibraryUnit _library; | 34 final LibraryUnit _library; |
47 final StrongTypeSystemImpl _typeSystem; | 35 final StrongTypeSystemImpl _typeSystem; |
48 | 36 |
49 CoercionReifier._( | 37 CoercionReifier(this._library, this._typeSystem); |
50 this._cm, this._tm, this._vm, this._library, this._typeSystem); | |
51 | |
52 factory CoercionReifier( | |
53 LibraryUnit library, StrongTypeSystemImpl typeSystem) { | |
54 var vm = new VariableManager(); | |
55 var tm = new TypeManager(library.library.element.enclosingElement, vm); | |
56 var cm = new CoercionManager(vm, tm); | |
57 return new CoercionReifier._(cm, tm, vm, library, typeSystem); | |
58 } | |
59 | 38 |
60 // This should be the entry point for this class. Entering via the | 39 // This should be the entry point for this class. Entering via the |
61 // visit functions directly may not do the right thing with respect | 40 // visit functions directly may not do the right thing with respect |
62 // to discharging the collected definitions. | 41 // to discharging the collected definitions. |
63 // Returns the set of new type identifiers added by the reifier | 42 // Returns the set of new type identifiers added by the reifier |
64 Map<Identifier, NewTypeIdDesc> reify() { | 43 void reify() { |
65 _library.partsThenLibrary.forEach(visitCompilationUnit); | 44 _library.partsThenLibrary.forEach(visitCompilationUnit); |
66 return _tm.addedTypes; | |
67 } | 45 } |
68 | 46 |
69 @override | 47 @override |
70 Object visitExpression(Expression node) { | 48 Object visitExpression(Expression node) { |
71 var info = CoercionInfo.get(node); | 49 var info = CoercionInfo.get(node); |
72 if (info is InferredTypeBase) { | 50 if (info is InferredTypeBase) { |
73 return _visitInferredTypeBase(info, node); | 51 return _visitInferredTypeBase(info, node); |
74 } else if (info is DownCast) { | 52 } else if (info is DownCast) { |
75 return _visitDownCast(info, node); | 53 return _visitDownCast(info, node); |
76 } | 54 } |
(...skipping 11 matching lines...) Expand all Loading... |
88 CoercionInfo.set(expr, info); | 66 CoercionInfo.set(expr, info); |
89 } | 67 } |
90 } | 68 } |
91 expr.visitChildren(this); | 69 expr.visitChildren(this); |
92 return null; | 70 return null; |
93 } | 71 } |
94 | 72 |
95 Object _visitDownCast(DownCast node, Expression expr) { | 73 Object _visitDownCast(DownCast node, Expression expr) { |
96 var parent = expr.parent; | 74 var parent = expr.parent; |
97 expr.visitChildren(this); | 75 expr.visitChildren(this); |
98 Expression newE = _cm.coerceExpression(expr, node.cast); | 76 Expression newE = coerceExpression(expr, node.cast); |
99 if (!identical(expr, newE)) { | 77 if (!identical(expr, newE)) { |
100 var replaced = parent.accept(new NodeReplacer(expr, newE)); | 78 var replaced = parent.accept(new NodeReplacer(expr, newE)); |
101 // It looks like NodeReplacer will always return true. | 79 // It looks like NodeReplacer will always return true. |
102 // It does throw IllegalArgumentException though, if child is not found. | 80 // It does throw IllegalArgumentException though, if child is not found. |
103 assert(replaced); | 81 assert(replaced); |
104 } | 82 } |
105 return null; | 83 return null; |
106 } | 84 } |
107 | 85 |
108 Object visitCompilationUnit(CompilationUnit unit) { | |
109 _cm.enterCompilationUnit(unit); | |
110 Object ret = super.visitCompilationUnit(unit); | |
111 _cm.exitCompilationUnit(unit); | |
112 return ret; | |
113 } | |
114 | |
115 DartType _getStaticType(Expression expr) { | 86 DartType _getStaticType(Expression expr) { |
116 return expr.staticType ?? DynamicTypeImpl.instance; | 87 return expr.staticType ?? DynamicTypeImpl.instance; |
117 } | 88 } |
118 } | |
119 | 89 |
120 // This provides a placeholder variable manager. Currently it simply | 90 /// Coerce [e] using [c], returning a new expression. |
121 // mangles names in a way unlikely (but not guaranteed) to avoid | |
122 // collisions with user variables. | |
123 // TODO(leafp): Replace this with something real. | |
124 class VariableManager { | |
125 // TODO(leafp): Hack, not for real. | |
126 int _id = 0; | |
127 | |
128 SimpleIdentifier freshIdentifier(String hint) { | |
129 String n = _id.toString(); | |
130 _id++; | |
131 String s = "__$hint$n"; | |
132 return AstBuilder.identifierFromString(s); | |
133 } | |
134 | |
135 SimpleIdentifier freshTypeIdentifier(String hint) { | |
136 return freshIdentifier(hint); | |
137 } | |
138 } | |
139 | |
140 // This class manages the reification of coercions as dart code. Given a | |
141 // coercion c and an expression e it will produce an expression e' which | |
142 // is the result of coercing e using c. | |
143 class CoercionManager { | |
144 VariableManager _vm; | |
145 TypeManager _tm; | |
146 | |
147 CoercionManager(this._vm, this._tm); | |
148 | |
149 // Call on entry to and exit from a compilation unit in order to properly | |
150 // discharge the accumulated wrappers. | |
151 void enterCompilationUnit(CompilationUnit unit) { | |
152 _tm.enterCompilationUnit(unit); | |
153 } | |
154 | |
155 void exitCompilationUnit(CompilationUnit unit) { | |
156 _tm.exitCompilationUnit(unit); | |
157 } | |
158 | |
159 // The main entry point. Coerce e using c, returning a new expression, | |
160 // possibly recording additional coercions functions and typedefs to | |
161 // be discharged at a higher level. | |
162 Expression coerceExpression(Expression e, Coercion c) { | 91 Expression coerceExpression(Expression e, Coercion c) { |
163 assert(c != null); | 92 assert(c != null); |
164 assert(c is! CoercionError); | 93 assert(c is! CoercionError); |
165 if (e is NamedExpression) { | 94 if (e is NamedExpression) { |
166 Expression inner = coerceExpression(e.expression, c); | 95 Expression inner = coerceExpression(e.expression, c); |
167 return new NamedExpression(e.name, inner); | 96 return new NamedExpression(e.name, inner); |
168 } | 97 } |
169 if (c is Cast) return _castExpression(e, c); | 98 if (c is Cast) return _castExpression(e, c); |
170 assert(c is Identity); | 99 assert(c is Identity); |
171 return e; | 100 return e; |
172 } | 101 } |
173 | 102 |
174 ///////////////// Private ////////////////////////////////// | 103 ///////////////// Private ////////////////////////////////// |
175 | 104 |
176 Expression _castExpression(Expression e, Cast c) { | 105 Expression _castExpression(Expression e, Cast c) { |
177 var ttName = _tm.typeNameFromDartType(c.toType); | 106 // We use an empty name in the AST, because the JS code generator only cares |
178 var cast = AstBuilder.asExpression(e, ttName); | 107 // about the target type. It does not look at the AST name. |
| 108 var typeName = new TypeName(AstBuilder.identifierFromString(''), null); |
| 109 typeName.type = c.toType; |
| 110 var cast = AstBuilder.asExpression(e, typeName); |
179 cast.staticType = c.toType; | 111 cast.staticType = c.toType; |
180 return cast; | 112 return cast; |
181 } | 113 } |
182 } | 114 } |
183 | |
184 // A class for managing the interaction between the DartType hierarchy | |
185 // and the AST type representation. It provides utilities to translate | |
186 // a DartType to AST. In order to do so, it maintains a map of typedefs | |
187 // naming otherwise un-named types. These must be discharged at the top | |
188 // level of the compilation unit in order to produce well-formed dart code. | |
189 // Note that in order to hoist the typedefs out of parameterized classes | |
190 // we must close over any type variables. | |
191 class TypeManager { | |
192 final VariableManager _vm; | |
193 final LibraryElement _currentLibrary; | |
194 final Map<Identifier, NewTypeIdDesc> addedTypes = {}; | |
195 CompilationUnitElement _currentUnit; | |
196 | |
197 /// A map containing new function typedefs to be introduced at the top level | |
198 /// This uses LinkedHashMap to emit code in a consistent order. | |
199 final Map<FunctionType, FunctionTypeAlias> _typedefs = {}; | |
200 | |
201 TypeManager(this._currentLibrary, this._vm); | |
202 | |
203 void enterCompilationUnit(CompilationUnit unit) { | |
204 _currentUnit = unit.element; | |
205 } | |
206 | |
207 void exitCompilationUnit(CompilationUnit unit) { | |
208 unit.declarations.addAll(_typedefs.values); | |
209 _typedefs.clear(); | |
210 } | |
211 | |
212 TypeName typeNameFromDartType(DartType dType) { | |
213 return _typeNameFromDartType(dType); | |
214 } | |
215 | |
216 NormalFormalParameter typedFormal(Identifier v, DartType type) { | |
217 return _typedFormal(v, type); | |
218 } | |
219 | |
220 ///////////////// Private ////////////////////////////////// | |
221 List<TypeParameterType> _freeTypeVariables(DartType type) { | |
222 var s = new Set<TypeParameterType>(); | |
223 | |
224 void _ft(DartType type) { | |
225 void _ftMap(Map<String, DartType> m) { | |
226 if (m == null) return; | |
227 for (var k in m.keys) _ft(m[k]); | |
228 } | |
229 void _ftList(List<DartType> l) { | |
230 if (l == null) return; | |
231 for (int i = 0; i < l.length; i++) _ft(l[i]); | |
232 } | |
233 | |
234 if (type == null) return; | |
235 if (type.isDynamic) return; | |
236 if (type.isBottom) return; | |
237 if (type.isObject) return; | |
238 if (type is TypeParameterType) { | |
239 s.add(type); | |
240 return; | |
241 } | |
242 if (type is ParameterizedType) { | |
243 if (type.name != null && type.name != "") { | |
244 _ftList(type.typeArguments); | |
245 return; | |
246 } | |
247 if (type is FunctionType) { | |
248 _ftMap(type.namedParameterTypes); | |
249 _ftList(type.normalParameterTypes); | |
250 _ftList(type.optionalParameterTypes); | |
251 _ft(type.returnType); | |
252 return; | |
253 } | |
254 assert(type is! InterfaceType); | |
255 assert(false); | |
256 } | |
257 if (type is VoidType) return; | |
258 print(type.toString()); | |
259 assert(false); | |
260 } | |
261 _ft(type); | |
262 return s.toList(); | |
263 } | |
264 | |
265 List<FormalParameter> _formalParameterListForFunctionType(FunctionType type) { | |
266 var namedParameters = type.namedParameterTypes; | |
267 var normalParameters = type.normalParameterTypes; | |
268 var optionalParameters = type.optionalParameterTypes; | |
269 var params = new List<FormalParameter>(); | |
270 for (int i = 0; i < normalParameters.length; i++) { | |
271 FormalParameter fp = | |
272 AstBuilder.requiredFormal(_anonymousFormal(normalParameters[i])); | |
273 _resolveFormal(fp, normalParameters[i]); | |
274 params.add(fp); | |
275 } | |
276 for (int i = 0; i < optionalParameters.length; i++) { | |
277 FormalParameter fp = | |
278 AstBuilder.optionalFormal(_anonymousFormal(optionalParameters[i])); | |
279 _resolveFormal(fp, optionalParameters[i]); | |
280 params.add(fp); | |
281 } | |
282 for (String k in namedParameters.keys) { | |
283 FormalParameter fp = | |
284 AstBuilder.namedFormal(_anonymousFormal(namedParameters[k])); | |
285 _resolveFormal(fp, namedParameters[k]); | |
286 params.add(fp); | |
287 } | |
288 return params; | |
289 } | |
290 | |
291 void _resolveFormal(FormalParameter fp, DartType type) { | |
292 ParameterElementImpl fe = new ParameterElementImpl.forNode(fp.identifier); | |
293 fe.parameterKind = fp.kind; | |
294 fe.type = type; | |
295 fp.identifier.staticElement = fe; | |
296 fp.identifier.staticType = type; | |
297 } | |
298 | |
299 FormalParameter _functionTypedFormal(Identifier v, FunctionType type) { | |
300 assert(v != null); | |
301 var params = _formalParameterListForFunctionType(type); | |
302 var ret = typeNameFromDartType(type.returnType); | |
303 return AstBuilder.functionTypedFormal(ret, v, params); | |
304 } | |
305 | |
306 NormalFormalParameter _anonymousFormal(DartType type) { | |
307 Identifier u = _vm.freshIdentifier("u"); | |
308 return _typedFormal(u, type); | |
309 } | |
310 | |
311 NormalFormalParameter _typedFormal(Identifier v, DartType type) { | |
312 if (type is FunctionType) { | |
313 return _functionTypedFormal(v, type); | |
314 } | |
315 assert(type.name != null); | |
316 TypeName t = typeNameFromDartType(type); | |
317 return AstBuilder.simpleFormal(v, t); | |
318 } | |
319 | |
320 SimpleIdentifier freshTypeDefVariable(String hint) { | |
321 var t = _vm.freshTypeIdentifier(hint); | |
322 var desc = new NewTypeIdDesc( | |
323 fromCurrent: true, importedFrom: _currentLibrary, synthetic: true); | |
324 addedTypes[t] = desc; | |
325 return t; | |
326 } | |
327 | |
328 SimpleIdentifier typeParameterFromString(String name) => | |
329 AstBuilder.identifierFromString(name); | |
330 | |
331 SimpleIdentifier freshReferenceToNamedType(DartType type) { | |
332 var name = type.name; | |
333 assert(name != null); | |
334 var id = AstBuilder.identifierFromString(name); | |
335 var element = type.element; | |
336 id.staticElement = element; | |
337 var library = null; | |
338 // This can happen for types like (e.g.) void | |
339 if (element != null) library = element.library; | |
340 var desc = new NewTypeIdDesc( | |
341 fromCurrent: _currentLibrary == library, | |
342 importedFrom: library, | |
343 synthetic: false); | |
344 addedTypes[id] = desc; | |
345 return id; | |
346 } | |
347 | |
348 FunctionTypeAlias _newResolvedTypedef( | |
349 FunctionType type, List<TypeParameterType> ftvs) { | |
350 // The name of the typedef (unresolved at this point) | |
351 // TODO(leafp): better naming. | |
352 SimpleIdentifier t = freshTypeDefVariable("CastType"); | |
353 | |
354 // The element for the new typedef | |
355 var element = new FunctionTypeAliasElementImpl(t.name, 0); | |
356 | |
357 // Fresh type parameter identifiers for the free type variables | |
358 List<Identifier> tNames = | |
359 ftvs.map((x) => typeParameterFromString(x.name)).toList(); | |
360 // The type parameters themselves | |
361 List<TypeParameter> tps = tNames.map(AstBuilder.typeParameter).toList(); | |
362 // Allocate the elements for the type parameters, fill in their | |
363 // type (which makes no sense) and link up the various elements | |
364 // For each type parameter identifier, make an element and a type | |
365 // with that element, link the two together, set the identifier element | |
366 // to that element, and the identifier type to that type. | |
367 List<TypeParameterElement> tElements = tNames.map((x) { | |
368 var element = new TypeParameterElementImpl(x.name, 0); | |
369 var type = new TypeParameterTypeImpl(element); | |
370 element.type = type; | |
371 x.staticElement = element; | |
372 x.staticType = type; | |
373 return element; | |
374 }).toList(); | |
375 // Get the types out from the elements | |
376 List<TypeParameterType> tTypes = tElements.map((x) => x.type).toList(); | |
377 // Take the return type from the original type, and replace the free | |
378 // type variables with the fresh type variables | |
379 element.returnType = type.returnType.substitute2(tTypes, ftvs); | |
380 // Set the type parameter elements | |
381 element.typeParameters = tElements; | |
382 // Set the parent element to the current compilation unit | |
383 element.enclosingElement = _currentUnit; | |
384 | |
385 // This is the type corresponding to the typedef. Note that | |
386 // almost all methods on this type delegate to the element, so it | |
387 // cannot be safely be used for anything until the element is fully resolved | |
388 FunctionTypeImpl substType = new FunctionTypeImpl.forTypedef(element); | |
389 element.type = substType; | |
390 // Link the type and the element into the identifier for the typedef | |
391 t.staticType = substType; | |
392 t.staticElement = element; | |
393 | |
394 // Make the formal parameters for the typedef, using the original type | |
395 // with the fresh type variables substituted in. | |
396 List<FormalParameter> fps = | |
397 _formalParameterListForFunctionType(type.substitute2(tTypes, ftvs)); | |
398 // Get the static elements out of the parameters, and use them to | |
399 // initialize the parameters in the element model | |
400 element.parameters = fps.map((x) => x.identifier.staticElement).toList(); | |
401 // Build the return type syntax | |
402 TypeName ret = _typeNameFromDartType(substType.returnType); | |
403 // This should now be fully resolved (or at least enough so for things | |
404 // to work so far). | |
405 FunctionTypeAlias alias = AstBuilder.functionTypeAlias(ret, t, tps, fps); | |
406 | |
407 return alias; | |
408 } | |
409 | |
410 // I think we can avoid alpha-varying type parameters, since | |
411 // the binding forms are so limited, so we just re-use the | |
412 // the original names for the formals and the actuals. | |
413 TypeName _typeNameFromFunctionType(FunctionType type) { | |
414 if (_typedefs.containsKey(type)) { | |
415 var alias = _typedefs[type]; | |
416 var ts = null; | |
417 var tpl = alias.typeParameters; | |
418 if (tpl != null) { | |
419 var ltp = tpl.typeParameters; | |
420 ts = new List<TypeName>.from( | |
421 ltp.map((t) => _mkNewTypeName(null, t.name, null))); | |
422 } | |
423 var name = alias.name; | |
424 return _mkNewTypeName(type, name, ts); | |
425 } | |
426 | |
427 List<TypeParameterType> ftvs = _freeTypeVariables(type); | |
428 FunctionTypeAlias alias = _newResolvedTypedef(type, ftvs); | |
429 _typedefs[type] = alias; | |
430 | |
431 List<TypeName> args = ftvs.map(_typeNameFromDartType).toList(); | |
432 TypeName namedType = | |
433 _mkNewTypeName(alias.name.staticType, alias.name, args); | |
434 | |
435 return namedType; | |
436 } | |
437 | |
438 TypeName _typeNameFromDartType(DartType dType) { | |
439 String name = dType.name; | |
440 if (name == null || name == "" || dType.isBottom) { | |
441 if (dType is FunctionType) return _typeNameFromFunctionType(dType); | |
442 _log.severe("No name for type, casting through dynamic"); | |
443 var d = AstBuilder.identifierFromString("dynamic"); | |
444 var t = _mkNewTypeName(dType, d, null); | |
445 return t; | |
446 } | |
447 SimpleIdentifier id = freshReferenceToNamedType(dType); | |
448 List<TypeName> args = null; | |
449 if (dType is ParameterizedType) { | |
450 List<DartType> targs = dType.typeArguments; | |
451 args = targs.map(_typeNameFromDartType).toList(); | |
452 } | |
453 var t = _mkNewTypeName(dType, id, args); | |
454 return t; | |
455 } | |
456 | |
457 TypeName _mkNewTypeName(DartType type, Identifier id, List<TypeName> args) { | |
458 var t = AstBuilder.typeName(id, args); | |
459 t.type = type; | |
460 return t; | |
461 } | |
462 } | |
OLD | NEW |