| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 /** | 5 /** |
| 6 * Code transform for @observable. The core transformation is relatively | 6 * Code transform for @observable. The core transformation is relatively |
| 7 * straightforward, and essentially like an editor refactoring. You can find the | 7 * straightforward, and essentially like an editor refactoring. You can find the |
| 8 * core implementation in [transformClass], which is ultimately called by | 8 * core implementation in [transformClass], which is ultimately called by |
| 9 * [transformObservables], the entry point to this library. | 9 * [transformObservables], the entry point to this library. |
| 10 */ | 10 */ |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 79 | 79 |
| 80 void transformClass(ClassDeclaration cls, TextEditTransaction code, | 80 void transformClass(ClassDeclaration cls, TextEditTransaction code, |
| 81 SourceFile file, Messages messages) { | 81 SourceFile file, Messages messages) { |
| 82 | 82 |
| 83 if (hasObservable(cls)) { | 83 if (hasObservable(cls)) { |
| 84 messages.warning('@observable on a class no longer has any effect. ' | 84 messages.warning('@observable on a class no longer has any effect. ' |
| 85 'It should be placed on individual fields.', | 85 'It should be placed on individual fields.', |
| 86 _getSpan(file, cls)); | 86 _getSpan(file, cls)); |
| 87 } | 87 } |
| 88 | 88 |
| 89 // We'd like to track whether observable was declared explicitly, otherwise |
| 90 // report a warning later below. Because we don't have type analysis (only |
| 91 // syntactic understanding of the code), we only report warnings that are |
| 92 // known to be true. |
| 93 var declaresObservable = false; |
| 94 if (cls.extendsClause != null) { |
| 95 var id = _getSimpleIdentifier(cls.extendsClause.superclass.name); |
| 96 if (id.name == 'ObservableBase') { |
| 97 code.edit(id.offset, id.end, 'ChangeNotifierBase'); |
| 98 declaresObservable = true; |
| 99 } else if (id.name == 'ChangeNotifierBase') { |
| 100 declaresObservable = true; |
| 101 } else if (id.name != 'PolymerElement' && id.name != 'CustomElement' |
| 102 && id.name != 'Object') { |
| 103 // TODO(sigmund): this is conservative, consider using type-resolution to |
| 104 // improve this check. |
| 105 declaresObservable = true; |
| 106 } |
| 107 } |
| 108 |
| 109 if (cls.withClause != null) { |
| 110 for (var type in cls.withClause.mixinTypes) { |
| 111 var id = _getSimpleIdentifier(type.name); |
| 112 if (id.name == 'ObservableMixin') { |
| 113 code.edit(id.offset, id.end, 'ChangeNotifierMixin'); |
| 114 declaresObservable = true; |
| 115 break; |
| 116 } else if (id.name == 'ChangeNotifierMixin') { |
| 117 declaresObservable = true; |
| 118 break; |
| 119 } else { |
| 120 // TODO(sigmund): this is conservative, consider using type-resolution |
| 121 // to improve this check. |
| 122 declaresObservable = true; |
| 123 } |
| 124 } |
| 125 } |
| 126 |
| 127 if (!declaresObservable && cls.implementsClause != null) { |
| 128 // TODO(sigmund): consider adding type-resolution to give a more precise |
| 129 // answer. |
| 130 declaresObservable = true; |
| 131 } |
| 132 |
| 89 // Track fields that were transformed. | 133 // Track fields that were transformed. |
| 90 var instanceFields = new Set<String>(); | 134 var instanceFields = new Set<String>(); |
| 91 var getters = new List<String>(); | 135 var getters = new List<String>(); |
| 92 var setters = new List<String>(); | 136 var setters = new List<String>(); |
| 93 | 137 |
| 94 for (var member in cls.members) { | 138 for (var member in cls.members) { |
| 95 if (member is FieldDeclaration) { | 139 if (member is FieldDeclaration) { |
| 96 bool isStatic = hasKeyword(member.keyword, Keyword.STATIC); | 140 bool isStatic = hasKeyword(member.keyword, Keyword.STATIC); |
| 97 if (isStatic) { | 141 if (isStatic) { |
| 98 if (hasObservable(member)){ | 142 if (hasObservable(member)){ |
| 99 messages.warning('Static fields can no longer be observable. ' | 143 messages.warning('Static fields can no longer be observable. ' |
| 100 'Observable fields should be put in an observable objects.', | 144 'Observable fields should be put in an observable objects.', |
| 101 _getSpan(file, member)); | 145 _getSpan(file, member)); |
| 102 } | 146 } |
| 103 continue; | 147 continue; |
| 104 } | 148 } |
| 105 if (hasObservable(member)) { | 149 if (hasObservable(member)) { |
| 150 if (!declaresObservable) { |
| 151 messages.warning('Observable fields should be put in an observable' |
| 152 ' objects. Please declare that this class extends from ' |
| 153 'ObservableBase, includes ObservableMixin, or implements ' |
| 154 'Observable.', |
| 155 _getSpan(file, member)); |
| 156 |
| 157 } |
| 106 transformFields(member.fields, code, member.offset, member.end); | 158 transformFields(member.fields, code, member.offset, member.end); |
| 107 | 159 |
| 108 var names = member.fields.variables.map((v) => v.name.name); | 160 var names = member.fields.variables.map((v) => v.name.name); |
| 109 | 161 |
| 110 getters.addAll(names); | 162 getters.addAll(names); |
| 111 if (!_isReadOnly(member.fields)) { | 163 if (!_isReadOnly(member.fields)) { |
| 112 setters.addAll(names); | 164 setters.addAll(names); |
| 113 instanceFields.addAll(names); | 165 instanceFields.addAll(names); |
| 114 } | 166 } |
| 115 } | 167 } |
| (...skipping 17 matching lines...) Expand all Loading... |
| 133 } | 185 } |
| 134 | 186 |
| 135 // Fix initializers, because they aren't allowed to call the setter. | 187 // Fix initializers, because they aren't allowed to call the setter. |
| 136 for (var member in cls.members) { | 188 for (var member in cls.members) { |
| 137 if (member is ConstructorDeclaration) { | 189 if (member is ConstructorDeclaration) { |
| 138 fixConstructor(member, code, instanceFields); | 190 fixConstructor(member, code, instanceFields); |
| 139 } | 191 } |
| 140 } | 192 } |
| 141 } | 193 } |
| 142 | 194 |
| 195 SimpleIdentifier _getSimpleIdentifier(Identifier id) => |
| 196 id is PrefixedIdentifier ? id.identifier : id; |
| 197 |
| 143 | 198 |
| 144 /** | 199 /** |
| 145 * Generates `getValueWorkaround` and `setValueWorkaround`. These will go away | 200 * Generates `getValueWorkaround` and `setValueWorkaround`. These will go away |
| 146 * shortly once dart2js supports mirrors. For the moment they provide something | 201 * shortly once dart2js supports mirrors. For the moment they provide something |
| 147 * that the binding system can use. | 202 * that the binding system can use. |
| 148 */ | 203 */ |
| 149 void mirrorWorkaround(ClassDeclaration cls, TextEditTransaction code, | 204 void mirrorWorkaround(ClassDeclaration cls, TextEditTransaction code, |
| 150 List<String> getters, List<String> setters) { | 205 List<String> getters, List<String> setters) { |
| 151 | 206 |
| 152 var sb = new StringBuffer('\ngetValueWorkaround(key) {\n'); | 207 var sb = new StringBuffer('\ngetValueWorkaround(key) {\n'); |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 261 $type __\$$name$initializer; | 316 $type __\$$name$initializer; |
| 262 $type get $name => __\$$name; | 317 $type get $name => __\$$name; |
| 263 set $name($type value) { | 318 set $name($type value) { |
| 264 __\$$name = notifyPropertyChange(const Symbol('$name'), __\$$name, value); | 319 __\$$name = notifyPropertyChange(const Symbol('$name'), __\$$name, value); |
| 265 } | 320 } |
| 266 '''.replaceAll('\n', '\n$indent')); | 321 '''.replaceAll('\n', '\n$indent')); |
| 267 } | 322 } |
| 268 | 323 |
| 269 code.edit(begin, end, '$replace'); | 324 code.edit(begin, end, '$replace'); |
| 270 } | 325 } |
| OLD | NEW |