Skip to content

Commit bf929d6

Browse files
kevmoomatanlurey
authored andcommitted
Add types to ConstantReader, fix issue with parts, migrate annotation helpers (#184)
* ConstantReader: Add Symbol, Type, double * urlOfElement: use librarySource.uri aligns with URL behavior of _MirrorTypeChecker - which also returns the library URI (not the part) * Migrate annotation helpers to TypeChecker and ConstantReader
1 parent 6f252d3 commit bf929d6

File tree

5 files changed

+135
-114
lines changed

5 files changed

+135
-114
lines changed

lib/src/annotation.dart

Lines changed: 31 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,22 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
library source_gen.annotation;
6-
7-
import 'dart:io';
85
import 'dart:mirrors';
96

107
import 'package:analyzer/dart/ast/ast.dart';
118
import 'package:analyzer/dart/element/element.dart';
129
import 'package:analyzer/dart/element/type.dart';
1310
import 'package:analyzer/src/dart/element/element.dart';
1411
import 'package:analyzer/src/generated/constant.dart';
15-
import 'package:analyzer/src/generated/resolver.dart';
1612
import 'package:analyzer/src/generated/utilities_dart.dart';
17-
import 'package:path/path.dart' as p;
13+
14+
import 'constants.dart';
15+
import 'type_checker.dart';
1816

1917
dynamic instantiateAnnotation(ElementAnnotation annotation) {
2018
var annotationObject = annotation.constantValue;
2119
try {
22-
return _getValue(annotationObject, annotation.element.context.typeProvider);
20+
return _getValue(annotation.constantValue);
2321
} on CannotCreateFromAnnotationException catch (e) {
2422
if (e.innerException != null) {
2523
// If there was a issue creating a nested object, there's not much we
@@ -51,33 +49,35 @@ dynamic instantiateAnnotation(ElementAnnotation annotation) {
5149
"${valueDeclaration.runtimeType}.");
5250
}
5351

54-
dynamic _getValue(DartObject object, TypeProvider typeProvider) {
55-
if (object.isNull) {
52+
dynamic _getValue(DartObject object) {
53+
var reader = new ConstantReader(object);
54+
55+
if (reader.isNull) {
5656
return null;
5757
}
5858

59-
if (object.type == typeProvider.boolType) {
60-
return object.toBoolValue();
59+
if (reader.isBool) {
60+
return reader.boolValue;
6161
}
6262

63-
if (object.type == typeProvider.intType) {
64-
return object.toIntValue();
63+
if (reader.isInt) {
64+
return reader.intValue;
6565
}
6666

67-
if (object.type == typeProvider.stringType) {
68-
return object.toStringValue();
67+
if (reader.isString) {
68+
return reader.stringValue;
6969
}
7070

71-
if (object.type == typeProvider.doubleType) {
72-
return object.toDoubleValue();
71+
if (reader.isDouble) {
72+
return reader.doubleValue;
7373
}
7474

75-
if (object.type == typeProvider.symbolType) {
76-
return new Symbol(object.toSymbolValue());
75+
if (reader.isSymbol) {
76+
return reader.symbolValue;
7777
}
7878

79-
if (object.type == typeProvider.typeType) {
80-
var typeData = object.toTypeValue();
79+
if (reader.isType) {
80+
var typeData = reader.typeValue;
8181

8282
if (typeData is InterfaceType) {
8383
var declarationMirror =
@@ -91,20 +91,17 @@ dynamic _getValue(DartObject object, TypeProvider typeProvider) {
9191
}
9292

9393
try {
94-
var listValue = object.toListValue();
95-
if (listValue != null) {
96-
return listValue
97-
.map((DartObject element) => _getValue(element, typeProvider))
98-
.toList();
99-
}
94+
if (reader.isList) {
95+
var listValue = reader.listValue;
10096

101-
var mapValue = object.toMapValue();
102-
if (mapValue != null) {
97+
return listValue.map((DartObject element) => _getValue(element)).toList();
98+
} else if (reader.isMap) {
99+
var mapValue = reader.mapValue;
103100
var result = {};
104101
mapValue.forEach((DartObject key, DartObject value) {
105-
dynamic mappedKey = _getValue(key, typeProvider);
102+
dynamic mappedKey = _getValue(key);
106103
if (mappedKey != null) {
107-
result[mappedKey] = _getValue(value, typeProvider);
104+
result[mappedKey] = _getValue(value);
108105
}
109106
});
110107
return result;
@@ -169,13 +166,11 @@ dynamic _createFromConstructor(
169166
fieldName = initializer.fieldName.name;
170167
}
171168

172-
var typeProvider = ctor.context.typeProvider;
173-
174169
var fieldObjectImpl = obj.fields[fieldName];
175170
if (p.parameterKind == ParameterKind.NAMED) {
176-
namedArgs[new Symbol(p.name)] = _getValue(fieldObjectImpl, typeProvider);
171+
namedArgs[new Symbol(p.name)] = _getValue(fieldObjectImpl);
177172
} else {
178-
positionalArgs.add(_getValue(fieldObjectImpl, typeProvider));
173+
positionalArgs.add(_getValue(fieldObjectImpl));
179174
}
180175
}
181176

@@ -225,81 +220,7 @@ bool matchAnnotation(Type annotationType, ElementAnnotation annotation) {
225220
'Could not determine type of annotation. Are you missing a dependency?');
226221
}
227222

228-
return matchTypes(annotationType, annotationValueType);
229-
}
230-
231-
/// Checks whether [annotationValueType] is equivalent to [annotationType].
232-
///
233-
/// Currently, this uses mirrors to compare the name and library uri of the two
234-
/// types.
235-
bool matchTypes(Type annotationType, ParameterizedType annotationValueType) {
236-
var classMirror = reflectClass(annotationType);
237-
var classMirrorSymbol = classMirror.simpleName;
238-
239-
var annTypeName = annotationValueType.name;
240-
var annotationTypeSymbol = new Symbol(annTypeName);
241-
242-
if (classMirrorSymbol != annotationTypeSymbol) {
243-
return false;
244-
}
245-
246-
var annotationLibSource = annotationValueType.element.library.source;
247-
248-
var libOwnerUri = (classMirror.owner as LibraryMirror).uri;
249-
var annotationLibSourceUri = annotationLibSource.uri;
250-
251-
if (annotationLibSourceUri.scheme == 'file' &&
252-
libOwnerUri.scheme == 'package') {
253-
// try to turn the libOwnerUri into a file uri
254-
libOwnerUri = _fileUriFromPackageUri(libOwnerUri);
255-
} else if (annotationLibSourceUri.scheme == 'asset' &&
256-
libOwnerUri.scheme == 'package') {
257-
// try to turn the libOwnerUri into a asset uri
258-
libOwnerUri = _assetUriFromPackageUri(libOwnerUri);
259-
}
260-
261-
return annotationLibSource.uri == libOwnerUri;
262-
}
263-
264-
Uri _fileUriFromPackageUri(Uri libraryPackageUri) {
265-
assert(libraryPackageUri.scheme == 'package');
266-
267-
var fullLibraryPath = p.join(_packageRoot, libraryPackageUri.path);
268-
269-
var file = new File(fullLibraryPath);
270-
271-
assert(file.existsSync());
272-
273-
var normalPath = file.resolveSymbolicLinksSync();
274-
275-
return new Uri.file(normalPath);
276-
}
277-
278-
Uri _assetUriFromPackageUri(Uri libraryPackageUri) {
279-
assert(libraryPackageUri.scheme == 'package');
280-
var originalSegments = libraryPackageUri.pathSegments;
281-
var newSegments = [originalSegments[0]]
282-
..add('lib')
283-
..addAll(originalSegments.getRange(1, originalSegments.length));
223+
var runtimeChecker = new TypeChecker.fromRuntime(annotationType);
284224

285-
return new Uri(scheme: 'asset', pathSegments: newSegments);
225+
return runtimeChecker.isExactlyType(annotationValueType);
286226
}
287-
288-
String get _packageRoot {
289-
if (_packageRootCache == null) {
290-
var dir = Platform.packageRoot;
291-
292-
if (dir.isEmpty) {
293-
dir = p.join(p.current, 'packages');
294-
}
295-
296-
// Handle the case where we're running via pub and dir is a file: URI
297-
dir = p.prettyUri(dir);
298-
299-
assert(FileSystemEntity.isDirectorySync(dir));
300-
_packageRootCache = dir;
301-
}
302-
return _packageRootCache;
303-
}
304-
305-
String _packageRootCache;

lib/src/constants.dart

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:analyzer/dart/constant/value.dart';
66
import 'package:analyzer/dart/element/element.dart';
7+
import 'package:analyzer/dart/element/type.dart';
78

89
import 'revive.dart';
910
import 'type_checker.dart';
@@ -98,6 +99,36 @@ abstract class ConstantReader {
9899
/// Throws [FormatException] if [isString] is `false`.
99100
String get stringValue;
100101

102+
/// Returns whether this constant represents a `double` literal.
103+
///
104+
/// If `true`, [doubleValue] will return a `double` (not throw).
105+
bool get isDouble;
106+
107+
/// Returns this constant as an `double` value.
108+
///
109+
/// Throws [FormatException] if [isDouble] is `false`.
110+
double get doubleValue;
111+
112+
/// Returns whether this constant represents a `Symbol` literal.
113+
///
114+
/// If `true`, [symbolValue] will return a `Symbol` (not throw).
115+
bool get isSymbol;
116+
117+
/// Returns this constant as an `Symbol` value.
118+
///
119+
/// Throws [FormatException] if [isSymbol] is `false`.
120+
Symbol get symbolValue;
121+
122+
/// Returns whether this constant represents a `Type` literal.
123+
///
124+
/// If `true`, [typeValue] will return a `DartType` (not throw).
125+
bool get isType;
126+
127+
/// Returns a [DartType] representing this as a `Type` value.
128+
///
129+
/// Throws [FormatException] if [isType] is `false`.
130+
DartType get typeValue;
131+
101132
/// Returns whether this constant represents `null`.
102133
bool get isNull;
103134

@@ -115,9 +146,8 @@ abstract class ConstantReader {
115146
Revivable revive();
116147
}
117148

118-
dynamic _throw(String expected, [dynamic object]) {
119-
throw new FormatException('Not a $expected', '$object');
120-
}
149+
dynamic _throw(String expected, [dynamic object]) => throw new FormatException(
150+
'Not an instance of $expected.', object == null ? null : '$object');
121151

122152
/// Implements a [ConstantReader] representing a `null` value.
123153
class _NullConstant implements ConstantReader {
@@ -159,6 +189,24 @@ class _NullConstant implements ConstantReader {
159189
@override
160190
bool get isString => false;
161191

192+
@override
193+
bool get isDouble => false;
194+
195+
@override
196+
double get doubleValue => _throw("double");
197+
198+
@override
199+
bool get isSymbol => false;
200+
201+
@override
202+
Symbol get symbolValue => _throw("Symbol");
203+
204+
@override
205+
get isType => false;
206+
207+
@override
208+
DartType get typeValue => _throw("Type");
209+
162210
@override
163211
bool instanceOf(TypeChecker checker) => false;
164212

@@ -180,6 +228,9 @@ class _Constant implements ConstantReader {
180228
_object.toBoolValue() ??
181229
_object.toIntValue() ??
182230
_object.toStringValue() ??
231+
_object.toDoubleValue() ??
232+
(isSymbol ? this.symbolValue : null) ??
233+
_object.toTypeValue() ??
183234
_object.toListValue() ??
184235
_object.toMapValue();
185236

@@ -220,6 +271,28 @@ class _Constant implements ConstantReader {
220271
@override
221272
bool get isString => _object.toStringValue() != null;
222273

274+
@override
275+
bool get isDouble => _object.toDoubleValue() != null;
276+
277+
@override
278+
double get doubleValue =>
279+
isDouble ? _object.toDoubleValue() : _throw('double', _object);
280+
281+
@override
282+
bool get isSymbol => _object.toSymbolValue() != null;
283+
284+
@override
285+
Symbol get symbolValue => isSymbol
286+
? new Symbol(_object.toSymbolValue())
287+
: _throw('Symbol', _object);
288+
289+
@override
290+
bool get isType => _object.toTypeValue() != null;
291+
292+
@override
293+
DartType get typeValue =>
294+
isType ? _object.toTypeValue() : _throw("Type", _object);
295+
223296
@override
224297
bool instanceOf(TypeChecker checker) =>
225298
checker.isAssignableFromType(_object.type);

lib/src/utils.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ String suggestLibraryName(AssetId source) {
7979
/// Returns a URL representing [element].
8080
String urlOfElement(Element element) => element.kind == ElementKind.DYNAMIC
8181
? 'dart:core#dynmaic'
82-
: normalizeUrl(element.source.uri)
82+
// using librarySource.uri – in case the element is in a part
83+
: normalizeUrl(element.librarySource.uri)
8384
.replace(fragment: element.name)
8485
.toString();
8586

test/annotation_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,9 @@ ElementAnnotation _mockElementAnnotation(String typeName, Uri libraryUri) {
250250
when(value.type).thenReturn(type);
251251
when(type.name).thenReturn(typeName);
252252
when(type.element).thenReturn(element);
253+
when(element.name).thenReturn(typeName);
253254
when(element.library).thenReturn(library);
255+
when(element.librarySource).thenReturn(source);
254256
when(library.source).thenReturn(source);
255257
when(source.uri).thenReturn(libraryUri);
256258
return annotation;

test/constants_test.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ void main() {
2222
const aNull = null;
2323
const aList = const [1, 2, 3];
2424
const aMap = const {1: 'A', 2: 'B'};
25+
const aDouble = 1.23;
26+
const aSymbol = #shanna;
27+
const aType = DateTime;
2528
2629
@aString // [0]
2730
@aInt // [1]
@@ -38,6 +41,9 @@ void main() {
3841
@aList // [6]
3942
@aMap // [7]
4043
@deprecated // [8]
44+
@aDouble // [9]
45+
@aSymbol // [10]
46+
@aType // [11]
4147
class Example {
4248
final String aString;
4349
final int aInt;
@@ -111,6 +117,24 @@ void main() {
111117
{1: 'A', 2: 'B'});
112118
});
113119

120+
test('should read a double', () {
121+
expect(constants[9].isDouble, isTrue);
122+
expect(constants[9].doubleValue, 1.23);
123+
expect(constants[9].anyValue, 1.23);
124+
});
125+
126+
test('should read a Symbol', () {
127+
expect(constants[10].isSymbol, isTrue);
128+
expect(constants[10].symbolValue, #shanna);
129+
expect(constants[10].anyValue, #shanna);
130+
});
131+
132+
test('should read a Type', () {
133+
expect(constants[11].isType, isTrue);
134+
expect(constants[11].typeValue.name, 'DateTime');
135+
expect(constants[11].anyValue.toString(), 'DateTime');
136+
});
137+
114138
test('should fail reading from `null`', () {
115139
final $null = constants[3];
116140
expect($null.isNull, isTrue, reason: '${$null}');

0 commit comments

Comments
 (0)