From fe36655c0a6b71870f713eb8459baa826b2d2577 Mon Sep 17 00:00:00 2001 From: David Morgan Date: Sun, 24 Sep 2017 15:50:50 +0200 Subject: [PATCH] Add workaround for dart2js issue with polymorphism. --- CHANGELOG.md | 4 +- .../lib/src/value_source_class.dart | 39 ++++++++++++++----- .../lib/src/value_source_class.g.dart | 4 ++ example/lib/polymorphism.dart | 17 ++++++++ example/lib/polymorphism.g.dart | 7 ++++ 5 files changed, 61 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91598a36..f530909f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ # Changelog -## 4.1.2 +## 4.2.0 - Generated code ignores more lints. +- Add a workaround so "polymorphism" features can be used with dart2js. + See example/lib/polymorphism.dart. ## 4.1.1 diff --git a/built_value_generator/lib/src/value_source_class.dart b/built_value_generator/lib/src/value_source_class.dart index e548b28c..f4f57cf5 100644 --- a/built_value_generator/lib/src/value_source_class.dart +++ b/built_value_generator/lib/src/value_source_class.dart @@ -29,6 +29,10 @@ abstract class ValueSourceClass @memoized ClassElement get builderElement => element.library.getType(name + 'Builder'); + @memoized + bool get implementsBuilt => element.allSupertypes + .any((interfaceType) => interfaceType.name == 'Built'); + @memoized BuiltValue get settings { final annotations = element.metadata @@ -146,9 +150,12 @@ abstract class ValueSourceClass static bool needsBuiltValue(ClassElement classElement) { // TODO(davidmorgan): more exact type check. - return classElement.allSupertypes - .any((interfaceType) => interfaceType.name == 'Built') && - !classElement.displayName.startsWith('_\$'); + return !classElement.displayName.startsWith('_\$') && + (classElement.allSupertypes + .any((interfaceType) => interfaceType.name == 'Built') || + classElement.metadata + .map((annotation) => annotation.computeConstantValue()) + .any((value) => value?.type?.displayName == 'BuiltValue')); } Iterable _computeErrors() { @@ -172,10 +179,13 @@ abstract class ValueSourceClass result.add('Make class abstract.'); } - final expectedBuildParameters = '$name$_generics, ${name}Builder$_generics'; - if (builtParameters != expectedBuildParameters) { - result.add('Make class implement Built<$expectedBuildParameters>. ' - 'Currently: Built<$builtParameters>'); + if (settings.instantiable) { + final expectedBuildParameters = + '$name$_generics, ${name}Builder$_generics'; + if (builtParameters != expectedBuildParameters) { + result.add('Make class implement Built<$expectedBuildParameters>. ' + 'Currently: Built<$builtParameters>'); + } } if (settings.instantiable) { @@ -552,8 +562,19 @@ abstract class ValueSourceClass /// Generates an abstract builder with just abstract setters and getters. String _generateAbstractBuilder() { final result = new StringBuffer(); - result.writeln('abstract class ${name}Builder$_boundedGenerics ' - 'implements ${builderImplements.join(", ")} {'); + + if (implementsBuilt) { + result.writeln('abstract class ${name}Builder$_boundedGenerics ' + 'implements ${builderImplements.join(", ")} {'); + } else { + // The "Built" interface has been omitted to work around dart2js issue + // https://github.com/dart-lang/sdk/issues/14729. So, we can't implement + // "Builder". Add the methods explicitly. + result.writeln('abstract class ${name}Builder$_boundedGenerics {'); + + result.writeln('void replace($name$_generics other);'); + result.writeln('void update(void updates(${name}Builder$_generics b));'); + } for (final field in fields) { final typeInBuilder = field.typeInBuilder; diff --git a/built_value_generator/lib/src/value_source_class.g.dart b/built_value_generator/lib/src/value_source_class.g.dart index 586619d4..818252e4 100644 --- a/built_value_generator/lib/src/value_source_class.g.dart +++ b/built_value_generator/lib/src/value_source_class.g.dart @@ -19,6 +19,7 @@ class _$ValueSourceClass extends ValueSourceClass { final ClassElement element; String __name; ClassElement __builderElement; + bool __implementsBuilt; BuiltValue __settings; BuiltList __genericParameters; BuiltList __genericBounds; @@ -50,6 +51,9 @@ class _$ValueSourceClass extends ValueSourceClass { @override ClassElement get builderElement => __builderElement ??= super.builderElement; + @override + bool get implementsBuilt => __implementsBuilt ??= super.implementsBuilt; + @override BuiltValue get settings => __settings ??= super.settings; diff --git a/example/lib/polymorphism.dart b/example/lib/polymorphism.dart index cf00f805..40379218 100644 --- a/example/lib/polymorphism.dart +++ b/example/lib/polymorphism.dart @@ -18,6 +18,9 @@ part 'polymorphism.g.dart'; /// Very little code is generated for a non-instantiable Built Value; just the /// interface for the builder. You can write this yourself if you prefer, then /// nothing will be generated. +/// +/// Note that this type of inheritance is currently not supported by dart2js. +/// See [Dart2jsCompatibleAnimal]. @BuiltValue(instantiable: false) abstract class Animal extends Object with Walker @@ -26,6 +29,20 @@ abstract class Animal extends Object int get legs; } +/// The dart2js issue https://github.com/dart-lang/sdk/issues/14729 +/// prevents this working when compiling to js. As a workaround, make `Animal` +/// not implement `Built`, and add the `rebuild` and `toBuilder` methods to +/// it explicitly if you need them. +@BuiltValue(instantiable: false) +abstract class Dart2jsCompatibleAnimal extends Object with Walker { + @override + int get legs; + + Dart2jsCompatibleAnimal rebuild( + void updates(Dart2jsCompatibleAnimalBuilder b)); + Dart2jsCompatibleAnimalBuilder toBuilder(); +} + /// `Cat` implements the non-instantiable Built Value `Animal`. The generated /// builder `CatBuilder` automatically extends 'AnimalBuilder`. This allows you /// to use `Cat` as an `Animal`. diff --git a/example/lib/polymorphism.g.dart b/example/lib/polymorphism.g.dart index 633e2eb6..7cf3f4c1 100644 --- a/example/lib/polymorphism.g.dart +++ b/example/lib/polymorphism.g.dart @@ -112,6 +112,13 @@ abstract class AnimalBuilder implements Builder { set legs(int legs); } +abstract class Dart2jsCompatibleAnimalBuilder { + void replace(Dart2jsCompatibleAnimal other); + void update(void updates(Dart2jsCompatibleAnimalBuilder b)); + int get legs; + set legs(int legs); +} + class _$Cat extends Cat { @override final bool tail;