-
Notifications
You must be signed in to change notification settings - Fork 110
Add types to ConstantReader, fix issue with parts, migrate annotation helpers #184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,24 +2,22 @@ | |
| // 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. | ||
|
|
||
| library source_gen.annotation; | ||
|
|
||
| import 'dart:io'; | ||
| import 'dart:mirrors'; | ||
|
|
||
| import 'package:analyzer/dart/ast/ast.dart'; | ||
| import 'package:analyzer/dart/element/element.dart'; | ||
| import 'package:analyzer/dart/element/type.dart'; | ||
| import 'package:analyzer/src/dart/element/element.dart'; | ||
| import 'package:analyzer/src/generated/constant.dart'; | ||
| import 'package:analyzer/src/generated/resolver.dart'; | ||
| import 'package:analyzer/src/generated/utilities_dart.dart'; | ||
| import 'package:path/path.dart' as p; | ||
|
|
||
| import 'constants.dart'; | ||
| import 'type_checker.dart'; | ||
|
|
||
| dynamic instantiateAnnotation(ElementAnnotation annotation) { | ||
| var annotationObject = annotation.constantValue; | ||
| try { | ||
| return _getValue(annotationObject, annotation.element.context.typeProvider); | ||
| return _getValue(annotation.constantValue); | ||
| } on CannotCreateFromAnnotationException catch (e) { | ||
| if (e.innerException != null) { | ||
| // If there was a issue creating a nested object, there's not much we | ||
|
|
@@ -51,33 +49,35 @@ dynamic instantiateAnnotation(ElementAnnotation annotation) { | |
| "${valueDeclaration.runtimeType}."); | ||
| } | ||
|
|
||
| dynamic _getValue(DartObject object, TypeProvider typeProvider) { | ||
| if (object.isNull) { | ||
| dynamic _getValue(DartObject object) { | ||
| var reader = new ConstantReader(object); | ||
|
|
||
| if (reader.isNull) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional: You can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sadly no, we need to talk about that. Type, List, and Map are weird – you get DartType. I think we should update |
||
| return null; | ||
| } | ||
|
|
||
| if (object.type == typeProvider.boolType) { | ||
| return object.toBoolValue(); | ||
| if (reader.isBool) { | ||
| return reader.boolValue; | ||
| } | ||
|
|
||
| if (object.type == typeProvider.intType) { | ||
| return object.toIntValue(); | ||
| if (reader.isInt) { | ||
| return reader.intValue; | ||
| } | ||
|
|
||
| if (object.type == typeProvider.stringType) { | ||
| return object.toStringValue(); | ||
| if (reader.isString) { | ||
| return reader.stringValue; | ||
| } | ||
|
|
||
| if (object.type == typeProvider.doubleType) { | ||
| return object.toDoubleValue(); | ||
| if (reader.isDouble) { | ||
| return reader.doubleValue; | ||
| } | ||
|
|
||
| if (object.type == typeProvider.symbolType) { | ||
| return new Symbol(object.toSymbolValue()); | ||
| if (reader.isSymbol) { | ||
| return reader.symbolValue; | ||
| } | ||
|
|
||
| if (object.type == typeProvider.typeType) { | ||
| var typeData = object.toTypeValue(); | ||
| if (reader.isType) { | ||
| var typeData = reader.typeValue; | ||
|
|
||
| if (typeData is InterfaceType) { | ||
| var declarationMirror = | ||
|
|
@@ -91,20 +91,17 @@ dynamic _getValue(DartObject object, TypeProvider typeProvider) { | |
| } | ||
|
|
||
| try { | ||
| var listValue = object.toListValue(); | ||
| if (listValue != null) { | ||
| return listValue | ||
| .map((DartObject element) => _getValue(element, typeProvider)) | ||
| .toList(); | ||
| } | ||
| if (reader.isList) { | ||
| var listValue = reader.listValue; | ||
|
|
||
| var mapValue = object.toMapValue(); | ||
| if (mapValue != null) { | ||
| return listValue.map((DartObject element) => _getValue(element)).toList(); | ||
| } else if (reader.isMap) { | ||
| var mapValue = reader.mapValue; | ||
| var result = {}; | ||
| mapValue.forEach((DartObject key, DartObject value) { | ||
| dynamic mappedKey = _getValue(key, typeProvider); | ||
| dynamic mappedKey = _getValue(key); | ||
| if (mappedKey != null) { | ||
| result[mappedKey] = _getValue(value, typeProvider); | ||
| result[mappedKey] = _getValue(value); | ||
| } | ||
| }); | ||
| return result; | ||
|
|
@@ -169,13 +166,11 @@ dynamic _createFromConstructor( | |
| fieldName = initializer.fieldName.name; | ||
| } | ||
|
|
||
| var typeProvider = ctor.context.typeProvider; | ||
|
|
||
| var fieldObjectImpl = obj.fields[fieldName]; | ||
| if (p.parameterKind == ParameterKind.NAMED) { | ||
| namedArgs[new Symbol(p.name)] = _getValue(fieldObjectImpl, typeProvider); | ||
| namedArgs[new Symbol(p.name)] = _getValue(fieldObjectImpl); | ||
| } else { | ||
| positionalArgs.add(_getValue(fieldObjectImpl, typeProvider)); | ||
| positionalArgs.add(_getValue(fieldObjectImpl)); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -225,81 +220,7 @@ bool matchAnnotation(Type annotationType, ElementAnnotation annotation) { | |
| 'Could not determine type of annotation. Are you missing a dependency?'); | ||
| } | ||
|
|
||
| return matchTypes(annotationType, annotationValueType); | ||
| } | ||
|
|
||
| /// Checks whether [annotationValueType] is equivalent to [annotationType]. | ||
| /// | ||
| /// Currently, this uses mirrors to compare the name and library uri of the two | ||
| /// types. | ||
| bool matchTypes(Type annotationType, ParameterizedType annotationValueType) { | ||
| var classMirror = reflectClass(annotationType); | ||
| var classMirrorSymbol = classMirror.simpleName; | ||
|
|
||
| var annTypeName = annotationValueType.name; | ||
| var annotationTypeSymbol = new Symbol(annTypeName); | ||
|
|
||
| if (classMirrorSymbol != annotationTypeSymbol) { | ||
| return false; | ||
| } | ||
|
|
||
| var annotationLibSource = annotationValueType.element.library.source; | ||
|
|
||
| var libOwnerUri = (classMirror.owner as LibraryMirror).uri; | ||
| var annotationLibSourceUri = annotationLibSource.uri; | ||
|
|
||
| if (annotationLibSourceUri.scheme == 'file' && | ||
| libOwnerUri.scheme == 'package') { | ||
| // try to turn the libOwnerUri into a file uri | ||
| libOwnerUri = _fileUriFromPackageUri(libOwnerUri); | ||
| } else if (annotationLibSourceUri.scheme == 'asset' && | ||
| libOwnerUri.scheme == 'package') { | ||
| // try to turn the libOwnerUri into a asset uri | ||
| libOwnerUri = _assetUriFromPackageUri(libOwnerUri); | ||
| } | ||
|
|
||
| return annotationLibSource.uri == libOwnerUri; | ||
| } | ||
|
|
||
| Uri _fileUriFromPackageUri(Uri libraryPackageUri) { | ||
| assert(libraryPackageUri.scheme == 'package'); | ||
|
|
||
| var fullLibraryPath = p.join(_packageRoot, libraryPackageUri.path); | ||
|
|
||
| var file = new File(fullLibraryPath); | ||
|
|
||
| assert(file.existsSync()); | ||
|
|
||
| var normalPath = file.resolveSymbolicLinksSync(); | ||
|
|
||
| return new Uri.file(normalPath); | ||
| } | ||
|
|
||
| Uri _assetUriFromPackageUri(Uri libraryPackageUri) { | ||
| assert(libraryPackageUri.scheme == 'package'); | ||
| var originalSegments = libraryPackageUri.pathSegments; | ||
| var newSegments = [originalSegments[0]] | ||
| ..add('lib') | ||
| ..addAll(originalSegments.getRange(1, originalSegments.length)); | ||
| var runtimeChecker = new TypeChecker.fromRuntime(annotationType); | ||
|
|
||
| return new Uri(scheme: 'asset', pathSegments: newSegments); | ||
| return runtimeChecker.isExactlyType(annotationValueType); | ||
| } | ||
|
|
||
| String get _packageRoot { | ||
| if (_packageRootCache == null) { | ||
| var dir = Platform.packageRoot; | ||
|
|
||
| if (dir.isEmpty) { | ||
| dir = p.join(p.current, 'packages'); | ||
| } | ||
|
|
||
| // Handle the case where we're running via pub and dir is a file: URI | ||
| dir = p.prettyUri(dir); | ||
|
|
||
| assert(FileSystemEntity.isDirectorySync(dir)); | ||
| _packageRootCache = dir; | ||
| } | ||
| return _packageRootCache; | ||
| } | ||
|
|
||
| String _packageRootCache; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
|
|
||
| import 'package:analyzer/dart/constant/value.dart'; | ||
| import 'package:analyzer/dart/element/element.dart'; | ||
| import 'package:analyzer/dart/element/type.dart'; | ||
|
|
||
| import 'revive.dart'; | ||
| import 'type_checker.dart'; | ||
|
|
@@ -98,6 +99,36 @@ abstract class ConstantReader { | |
| /// Throws [FormatException] if [isString] is `false`. | ||
| String get stringValue; | ||
|
|
||
| /// Returns whether this constant represents a `double` literal. | ||
| /// | ||
| /// If `true`, [doubleValue] will return a `double` (not throw). | ||
| bool get isDouble; | ||
|
|
||
| /// Returns this constant as an `double` value. | ||
| /// | ||
| /// Throws [FormatException] if [isDouble] is `false`. | ||
| double get doubleValue; | ||
|
|
||
| /// Returns whether this constant represents a `Symbol` literal. | ||
| /// | ||
| /// If `true`, [symbolValue] will return a `Symbol` (not throw). | ||
| bool get isSymbol; | ||
|
|
||
| /// Returns this constant as an `Symbol` value. | ||
| /// | ||
| /// Throws [FormatException] if [isSymbol] is `false`. | ||
| Symbol get symbolValue; | ||
|
|
||
| /// Returns whether this constant represents a `Type` literal. | ||
| /// | ||
| /// If `true`, [typeValue] will return a `DartType` (not throw). | ||
| bool get isType; | ||
|
|
||
| /// Returns a [DartType] representing this as a `Type` value. | ||
| /// | ||
| /// Throws [FormatException] if [isType] is `false`. | ||
| DartType get typeValue; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @matanlurey – can't get the actual |
||
|
|
||
| /// Returns whether this constant represents `null`. | ||
| bool get isNull; | ||
|
|
||
|
|
@@ -115,9 +146,8 @@ abstract class ConstantReader { | |
| Revivable revive(); | ||
| } | ||
|
|
||
| dynamic _throw(String expected, [dynamic object]) { | ||
| throw new FormatException('Not a $expected', '$object'); | ||
| } | ||
| dynamic _throw(String expected, [dynamic object]) => throw new FormatException( | ||
| 'Not an instance of $expected.', object == null ? null : '$object'); | ||
|
|
||
| /// Implements a [ConstantReader] representing a `null` value. | ||
| class _NullConstant implements ConstantReader { | ||
|
|
@@ -159,6 +189,24 @@ class _NullConstant implements ConstantReader { | |
| @override | ||
| bool get isString => false; | ||
|
|
||
| @override | ||
| bool get isDouble => false; | ||
|
|
||
| @override | ||
| double get doubleValue => _throw("double"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ' There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Huh? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh...geez... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
|
|
||
| @override | ||
| bool get isSymbol => false; | ||
|
|
||
| @override | ||
| Symbol get symbolValue => _throw("Symbol"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ' There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
|
|
||
| @override | ||
| get isType => false; | ||
|
|
||
| @override | ||
| DartType get typeValue => _throw("Type"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ' There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
|
|
||
| @override | ||
| bool instanceOf(TypeChecker checker) => false; | ||
|
|
||
|
|
@@ -180,6 +228,9 @@ class _Constant implements ConstantReader { | |
| _object.toBoolValue() ?? | ||
| _object.toIntValue() ?? | ||
| _object.toStringValue() ?? | ||
| _object.toDoubleValue() ?? | ||
| (isSymbol ? this.symbolValue : null) ?? | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Special case here to return an actual |
||
| _object.toTypeValue() ?? | ||
| _object.toListValue() ?? | ||
| _object.toMapValue(); | ||
|
|
||
|
|
@@ -220,6 +271,28 @@ class _Constant implements ConstantReader { | |
| @override | ||
| bool get isString => _object.toStringValue() != null; | ||
|
|
||
| @override | ||
| bool get isDouble => _object.toDoubleValue() != null; | ||
|
|
||
| @override | ||
| double get doubleValue => | ||
| isDouble ? _object.toDoubleValue() : _throw('double', _object); | ||
|
|
||
| @override | ||
| bool get isSymbol => _object.toSymbolValue() != null; | ||
|
|
||
| @override | ||
| Symbol get symbolValue => isSymbol | ||
| ? new Symbol(_object.toSymbolValue()) | ||
| : _throw('Symbol', _object); | ||
|
|
||
| @override | ||
| bool get isType => _object.toTypeValue() != null; | ||
|
|
||
| @override | ||
| DartType get typeValue => | ||
| isType ? _object.toTypeValue() : _throw("Type", _object); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consistent use of ' please. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done – enable a lint 😁 |
||
|
|
||
| @override | ||
| bool instanceOf(TypeChecker checker) => | ||
| checker.isAssignableFromType(_object.type); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK you always want to use
computeConstantValue.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just ran a little test – in all cases
constantValueandcomputeConstantValuereturn the same result. 🤷♂️