blob: 7229a1da9983d0bcd6ee110fca69d112d8ab61a9 [file]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/analyzer.dart' show ParameterKind;
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
import 'closure_annotation.dart';
import 'closure_type.dart';
/// Mixin that can generate [ClosureAnnotation]s for Dart elements and types.
abstract class ClosureAnnotator {
TypeProvider get types;
/// Must return a JavaScript qualified name that can be used to refer to [type].
String getQualifiedName(TypeDefiningElement type);
ClosureAnnotation closureAnnotationForVariable(VariableElement e) =>
new ClosureAnnotation(
type: _closureTypeForDartType(e.type),
// Note: we don't set isConst here because Closure's constness and
// Dart's are not really compatible.
isFinal: e.isFinal || e.isConst);
/// We don't use Closure's `@typedef` annotations
ClosureAnnotation closureAnnotationForTypeDef(FunctionTypeAliasElement e) =>
new ClosureAnnotation(
type: _closureTypeForDartType(e.type, forceTypeDefExpansion: true),
isTypedef: true);
ClosureAnnotation closureAnnotationForDefaultConstructor(ClassElement e) =>
new ClosureAnnotation(
superType: _closureTypeForDartType(e.supertype),
interfaces: e.interfaces.map(_closureTypeForDartType).toList());
// TODO(ochafik): Handle destructured params when Closure supports it.
ClosureAnnotation closureAnnotationFor(
ExecutableElement e, String namedArgsMapName) {
var paramTypes = <String, ClosureType>{};
var namedArgs = <String, ClosureType>{};
for (var param in e.parameters) {
var t = _closureTypeForDartType(param.type);
switch (param.parameterKind) {
case ParameterKind.NAMED:
namedArgs[param.name] = t.orUndefined();
break;
case ParameterKind.POSITIONAL:
paramTypes[param.name] = t.toOptional();
break;
case ParameterKind.REQUIRED:
paramTypes[param.name] = t;
break;
}
}
if (namedArgs.isNotEmpty) {
paramTypes[namedArgsMapName] =
new ClosureType.record(namedArgs).toOptional();
}
var returnType = e is ConstructorElement
? (e.isFactory ? _closureTypeForClass(e.enclosingElement) : null)
: _closureTypeForDartType(e.returnType);
return new ClosureAnnotation(
isOverride: e.isOverride,
// Note: Dart and Closure privacy are not compatible: don't set `isPrivate: e.isPrivate`.
paramTypes: paramTypes,
returnType: returnType);
}
Map<DartType, ClosureType> __commonTypes;
Map<DartType, ClosureType> get _commonTypes {
if (__commonTypes == null) {
var numberType = new ClosureType.number().toNullable();
__commonTypes = {
types.intType: numberType,
types.numType: numberType,
types.doubleType: numberType,
types.boolType: new ClosureType.boolean().toNullable(),
types.stringType: new ClosureType.string(),
};
}
return __commonTypes;
}
ClosureType _closureTypeForClass(ClassElement classElement, [DartType type]) {
ClosureType closureType = _commonTypes[type];
if (closureType != null) return closureType;
var fullName = _getFullName(classElement);
switch (fullName) {
// TODO(ochafik): Test DartTypes directly if possible.
case "dart.js.JsArray":
return new ClosureType.array(
type is InterfaceType && type.typeArguments.length == 1
? _closureTypeForDartType(type.typeArguments.single)
: null);
case "dart.js.JsObject":
return new ClosureType.map();
case "dart.js.JsFunction":
return new ClosureType.function();
default:
return new ClosureType.type(getQualifiedName(classElement));
}
}
ClosureType _closureTypeForDartType(DartType type,
{bool forceTypeDefExpansion: false}) {
if (type == null || type.isBottom || type.isDynamic) {
return new ClosureType.unknown();
}
if (type.isVoid) return null;
if (type is FunctionType) {
if (!forceTypeDefExpansion && type.element.name != '') {
return new ClosureType.type(getQualifiedName(type.element));
}
var args = []
..addAll(type.normalParameterTypes.map(_closureTypeForDartType))
..addAll(type.optionalParameterTypes
.map((t) => _closureTypeForDartType(t).toOptional()));
if (type.namedParameterTypes.isNotEmpty) {
var namedArgs = <String, ClosureType>{};
type.namedParameterTypes.forEach((n, t) {
namedArgs[n] = _closureTypeForDartType(t).orUndefined();
});
args.add(new ClosureType.record(namedArgs).toOptional());
}
return new ClosureType.function(
args, _closureTypeForDartType(type.returnType));
}
if (type is InterfaceType) {
return _closureTypeForClass(type.element, type);
}
return new ClosureType.unknown();
}
/// TODO(ochafik): Use a package-and-file-uri-dependent naming, since libraries can collide.
String _getFullName(ClassElement type) =>
type.library.name == '' ? type.name : '${type.library.name}.${type.name}';
}