| 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 |